You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ia...@apache.org on 2013/11/18 20:55:33 UTC

[1/6] git commit: CB-5408: Add handler for filesystem:// urls

Updated Branches:
  refs/heads/dev 1dfdf0f81 -> 7bb397771


CB-5408: Add handler for filesystem:// urls


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/b8c4f85e
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/b8c4f85e
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/b8c4f85e

Branch: refs/heads/dev
Commit: b8c4f85eb56ca51fec08cf8a1fbc926f56ac4e7e
Parents: b3b6a83
Author: Ian Clelland <ic...@chromium.org>
Authored: Fri Nov 8 15:11:13 2013 -0500
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Nov 18 14:53:40 2013 -0500

----------------------------------------------------------------------
 src/ios/CDVFile.m | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 55 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/b8c4f85e/src/ios/CDVFile.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVFile.m b/src/ios/CDVFile.m
index 2f41046..29a3241 100644
--- a/src/ios/CDVFile.m
+++ b/src/ios/CDVFile.m
@@ -25,6 +25,8 @@
 #import <MobileCoreServices/MobileCoreServices.h>
 #import <sys/xattr.h>
 
+static CDVFile *filePlugin = nil;
+
 @interface CDVFilesystemURL : NSObject  {
     NSURL *url_;
     CDVFileSystemType fileSystemType_;
@@ -71,6 +73,56 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
 NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 NSString* const kCDVAssetsLibraryScheme = @"assets-library";
 
+@interface CDVFilesystemURLProtocol : NSURLProtocol
+@end
+
+@implementation CDVFilesystemURLProtocol
+
+NSString* const kCDVFilesystemURLPrefix = @"filesystem";
+
++ (BOOL)canInitWithRequest:(NSURLRequest*)request
+{
+    NSURL* url = [request URL];
+    return [[url scheme] isEqualToString:kCDVFilesystemURLPrefix];
+}
+
++ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
+{
+    return request;
+}
+
++ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
+{
+    return [[[requestA URL] resourceSpecifier] isEqualToString:[[requestB URL] resourceSpecifier]];
+}
+
+- (void)startLoading
+{
+    NSURL *url = [[self request] URL];
+    NSString *pathString = [url relativePath];
+    pathString = [filePlugin fileSystemPathForLocalURI:url];
+    [CDVFile readFileWithPath:pathString start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) {
+        if (!error) {
+            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:@{@"Content-Type": mimetype}];
+            [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+            [[self client] URLProtocol:self didLoadData:data];
+            [[self client] URLProtocolDidFinishLoading:self];
+        } else {
+            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:@{}];
+            [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+            [[self client] URLProtocolDidFinishLoading:self];
+        }
+    }];
+}
+
+- (void)stopLoading
+{
+    // do any cleanup here
+}
+
+@end
+
+
 @implementation CDVFile
 
 @synthesize appDocsPath, appLibraryPath, appTempPath, persistentPath, temporaryPath, userHasAllowed;
@@ -79,6 +131,9 @@ NSString* const kCDVAssetsLibraryScheme = @"assets-library";
 {
     self = (CDVFile*)[super initWithWebView:theWebView];
     if (self) {
+        filePlugin = self;
+        [NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]];
+
         // get the documents directory path
         NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
         self.appDocsPath = [paths objectAtIndex:0];


[6/6] git commit: CB-5406: Split iOS file plugin into modules

Posted by ia...@apache.org.
CB-5406: Split iOS file plugin into modules


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/7bb39777
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/7bb39777
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/7bb39777

Branch: refs/heads/dev
Commit: 7bb397771431c24ae4fdbe4c5d5f745479689904
Parents: 64187ca
Author: Ian Clelland <ic...@chromium.org>
Authored: Mon Nov 18 14:52:14 2013 -0500
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Nov 18 14:54:01 2013 -0500

----------------------------------------------------------------------
 plugin.xml                          |   4 +
 src/ios/CDVAssetLibraryFilesystem.h |  28 +
 src/ios/CDVAssetLibraryFilesystem.m | 247 +++++++++
 src/ios/CDVFile.h                   |  37 +-
 src/ios/CDVFile.m                   | 878 +------------------------------
 src/ios/CDVLocalFilesystem.h        |  30 ++
 src/ios/CDVLocalFilesystem.m        | 655 +++++++++++++++++++++++
 7 files changed, 1003 insertions(+), 876 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/7bb39777/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index d4091f8..6743c1b 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -113,6 +113,10 @@ xmlns:android="http://schemas.android.com/apk/res/android"
         </config-file>
         <header-file src="src/ios/CDVFile.h" />
         <source-file src="src/ios/CDVFile.m" />
+        <header-file src="src/ios/CDVLocalFilesystem.h" />
+        <source-file src="src/ios/CDVLocalFilesystem.m" />
+        <header-file src="src/ios/CDVAssetLibraryFilesystem.h" />
+        <source-file src="src/ios/CDVAssetLibraryFilesystem.m" />
 
         <!-- ios specific file apis -->
         <js-module src="www/ios/Entry.js" name="iosEntry">

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/7bb39777/src/ios/CDVAssetLibraryFilesystem.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVAssetLibraryFilesystem.h b/src/ios/CDVAssetLibraryFilesystem.h
new file mode 100644
index 0000000..295a3d8
--- /dev/null
+++ b/src/ios/CDVAssetLibraryFilesystem.h
@@ -0,0 +1,28 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+ */
+
+#import "CDVFile.h"
+
+extern NSString* const kCDVAssetsLibraryPrefix;
+extern NSString* const kCDVAssetsLibraryScheme;
+
+@interface CDVAssetLibraryFilesystem : NSObject<CDVFileSystem> {
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/7bb39777/src/ios/CDVAssetLibraryFilesystem.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVAssetLibraryFilesystem.m b/src/ios/CDVAssetLibraryFilesystem.m
new file mode 100644
index 0000000..a266786
--- /dev/null
+++ b/src/ios/CDVAssetLibraryFilesystem.m
@@ -0,0 +1,247 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+ */
+
+#import "CDVFile.h"
+#import "CDVAssetLibraryFilesystem.h"
+#import <Cordova/CDV.h>
+#import <AssetsLibrary/ALAsset.h>
+#import <AssetsLibrary/ALAssetRepresentation.h>
+#import <AssetsLibrary/ALAssetsLibrary.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+
+NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
+NSString* const kCDVAssetsLibraryScheme = @"assets-library";
+
+@implementation CDVAssetLibraryFilesystem
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url
+{
+    NSDictionary* fileSystem = [self makeEntryForPath:[url.url absoluteString] fileSystem:ASSETS_LIBRARY isDirectory:NO];
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+}
+
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir
+{
+    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+    NSString* lastPart = [fullPath lastPathComponent];
+    if (isDir && ![fullPath hasSuffix:@"/"]) {
+        fullPath = [fullPath stringByAppendingString:@"/"];
+    }
+    [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
+    [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
+    [dirEntry setObject:fullPath forKey:@"fullPath"];
+    [dirEntry setObject:lastPart forKey:@"name"];
+    [dirEntry setObject: [NSNumber numberWithInt:fsType] forKey: @"filesystem"];
+
+    return dirEntry;
+}
+
+/* helper function to get the mimeType from the file extension
+ * IN:
+ *	NSString* fullPath - filename (may include path)
+ * OUT:
+ *	NSString* the mime type as type/subtype.  nil if not able to determine
+ */
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+    NSString* mimeType = nil;
+
+    if (fullPath) {
+        CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
+        if (typeId) {
+            mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
+            if (!mimeType) {
+                // special case for m4a
+                if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
+                    mimeType = @"audio/mp4";
+                } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
+                    mimeType = @"audio/wav";
+                }
+            }
+            CFRelease(typeId);
+        }
+    }
+    return mimeType;
+}
+
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
+{
+    // return unsupported result for assets-library URLs
+   return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
+{
+    // we don't (yet?) support getting the parent of an asset
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
+}
+
+- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback
+{
+    __block CDVPluginResult* result = nil;
+
+    // In this case, we need to use an asynchronous method to retrieve the file.
+    // Because of this, we can't just assign to `result` and send it at the end of the method.
+    // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+        if (asset) {
+            // We have the asset!  Retrieve the metadata and send it off.
+            NSDate* date = [asset valueForProperty:ALAssetPropertyDate];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[date timeIntervalSince1970] * 1000];
+            callback(result);
+        } else {
+            // We couldn't find the asset.  Send the appropriate error.
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+            callback(result);
+        }
+    };
+    // TODO(maxw): Consider making this a class variable since it's the same every time.
+    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+        // Retrieving the asset failed for some reason.  Send the appropriate error.
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+        callback(result);
+    };
+
+    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+    [assetsLibrary assetForURL:url.url resultBlock:resultBlock failureBlock:failureBlock];
+    return;
+}
+
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
+{
+    // setMetadata doesn't make sense for asset library files
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+}
+
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
+{
+    // return error for assets-library URLs
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+}
+
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
+{
+    // return error for assets-library URLs
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
+{
+    // return unsupported result for assets-library URLs
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
+{
+    // assets-library files can't be truncated
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+}
+
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
+{
+    // text can't be written into assets-library files
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+}
+
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
+{
+    // Copying to an assets library file is not doable, since we can't write it.
+    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+    callback(result);
+}
+
+- (NSString *)fileSystemPathForURL:(CDVFilesystemURL *)url
+{
+    NSString *path = nil;
+    if ([[url.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+        path = [url.url path];
+    } else {
+       path = url.fullPath;
+    }
+    if ([path hasSuffix:@"/"]) {
+      path = [path substringToIndex:([path length]-1)];
+    }
+    return path;
+}
+
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+
+    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+        if (asset) {
+            // We have the asset!  Get the data and send it off.
+            ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+            Byte* buffer = (Byte*)malloc([assetRepresentation size]);
+            NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
+            NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+            NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
+
+            callback(data, MIMEType, NO_ERROR);
+        } else {
+            callback(nil, nil, NOT_FOUND_ERR);
+        }
+    };
+
+    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+        // Retrieving the asset failed for some reason.  Send the appropriate error.
+        NSLog(@"Error: %@", error);
+        callback(nil, nil, SECURITY_ERR);
+    };
+
+    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+    [assetsLibrary assetForURL:[NSURL URLWithString:path] resultBlock:resultBlock failureBlock:failureBlock];
+}
+
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+
+    // In this case, we need to use an asynchronous method to retrieve the file.
+    // Because of this, we can't just assign to `result` and send it at the end of the method.
+    // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+        if (asset) {
+            // We have the asset!  Populate the dictionary and send it off.
+            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+            ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
+            [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
+            NSString* filename = [assetRepresentation filename];
+            [fileInfo setObject:filename forKey:@"name"];
+            [fileInfo setObject:[CDVAssetLibraryFilesystem getMimeTypeFromPath:filename] forKey:@"type"];
+            NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
+            NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
+            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
+        } else {
+            // We couldn't find the asset.  Send the appropriate error.
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
+        }
+    };
+    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+        // Retrieving the asset failed for some reason.  Send the appropriate error.
+        callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]);
+    };
+
+    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+    [assetsLibrary assetForURL:[NSURL URLWithString:path] resultBlock:resultBlock failureBlock:failureBlock];
+    return;
+}
+@end

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/7bb39777/src/ios/CDVFile.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVFile.h b/src/ios/CDVFile.h
index ef17a62..0f6d618 100644
--- a/src/ios/CDVFile.h
+++ b/src/ios/CDVFile.h
@@ -44,7 +44,42 @@ enum CDVFileSystemType {
 };
 typedef int CDVFileSystemType;
 
-extern NSString* const kCDVAssetsLibraryPrefix;
+@interface CDVFilesystemURL : NSObject  {
+    NSURL *_url;
+    CDVFileSystemType _fileSystemType;
+    NSString *_fullPath;
+}
+
+- (id) initWithString:(NSString*)strURL;
+- (id) initWithURL:(NSURL*)URL;
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL;
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL;
+
+
+@property (atomic) NSURL *url;
+@property (atomic) CDVFileSystemType fileSystemType;
+@property (atomic) NSString *fullPath;
+
+@end
+
+@interface CDVFilesystemURLProtocol : NSURLProtocol
+@end
+
+@protocol CDVFileSystem
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options;
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI;
+- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback;
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options;
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos;
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend;
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback;
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback;
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback;
+@end
 
 @interface CDVFile : CDVPlugin {
     NSString* appDocsPath;

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/7bb39777/src/ios/CDVFile.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVFile.m b/src/ios/CDVFile.m
index 47f32a5..b09b5f3 100644
--- a/src/ios/CDVFile.m
+++ b/src/ios/CDVFile.m
@@ -17,13 +17,10 @@
  under the License.
  */
 
-#import "CDVFile.h"
 #import <Cordova/CDV.h>
-#import <AssetsLibrary/ALAsset.h>
-#import <AssetsLibrary/ALAssetRepresentation.h>
-#import <AssetsLibrary/ALAssetsLibrary.h>
-#import <MobileCoreServices/MobileCoreServices.h>
-#import <sys/xattr.h>
+#import "CDVFile.h"
+#import "CDVLocalFilesystem.h"
+#import "CDVAssetLibraryFilesystem.h"
 
 static CDVFile *filePlugin = nil;
 
@@ -33,24 +30,6 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
     NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
 #endif
 
-NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
-NSString* const kCDVAssetsLibraryScheme = @"assets-library";
-
-@interface CDVFilesystemURL : NSObject  {
-    NSURL *_url;
-    CDVFileSystemType _fileSystemType;
-    NSString *_fullPath;
-}
-
-- (id) initWithString:(NSString*)strURL;
-- (id) initWithURL:(NSURL*)URL;
-
-@property (atomic) NSURL *url;
-@property (atomic) CDVFileSystemType fileSystemType;
-@property (atomic) NSString *fullPath;
-
-@end
-
 @implementation CDVFilesystemURL
 @synthesize url=_url;
 @synthesize fileSystemType=_fileSystemType;
@@ -125,857 +104,6 @@ NSString* const kCDVAssetsLibraryScheme = @"assets-library";
 
 @end
 
-@protocol CDVFileSystem
-- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
-- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options;
-- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI;
-- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback;
-- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options;
-- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI;
-- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI;
-- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI;
-- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos;
-- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend;
-- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback;
-- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback;
-- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback;
-@end
-
-@interface CDVLocalFilesystem : NSObject<CDVFileSystem> {
-    NSString *_fsRoot;
-}
-- (id) initWithName:(NSString *)name root:(NSString *)fsRoot;
-@property (nonatomic,strong) NSString *fsRoot;
-@end
-
-@implementation CDVLocalFilesystem
-@synthesize fsRoot=_fsRoot;
-
-- (id) initWithName:(NSString *)name root:(NSString *)fsRoot
-{
-    if (self) {
-        _fsRoot = fsRoot;
-    }
-    return self;
-}
-
-/*
- * IN
- *  NSString localURI
- * OUT
- *  CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the
- *   URI represents a non-existent path, or is unrecognized or otherwise malformed.
- */
-- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
-{
-    CDVPluginResult* result = nil;
-    NSString *path = [self fileSystemPathForURL:url];
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    BOOL isDir = NO;
-    // see if exists and is file or dir
-    BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir];
-    if (bExists) {
-        NSDictionary* fileSystem = [self makeEntryForPath:url.fullPath fileSystem:url.fileSystemType isDirectory:isDir];
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
-    } else {
-        // return NOT_FOUND_ERR
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-    }
-    return result;
-}
-- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir
-{
-    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
-    NSString* lastPart = [fullPath lastPathComponent];
-    if (isDir && ![fullPath hasSuffix:@"/"]) {
-        fullPath = [fullPath stringByAppendingString:@"/"];
-    }
-    [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
-    [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
-    [dirEntry setObject:fullPath forKey:@"fullPath"];
-    [dirEntry setObject:lastPart forKey:@"name"];
-    [dirEntry setObject: [NSNumber numberWithInt:fsType] forKey: @"filesystem"];
-
-    return dirEntry;
-}
-
-/*
- * IN
- *  NSString localURI
- * OUT
- *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
- *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
- *  or if the URL is malformed.
- * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
- */
-- (NSString *)fileSystemPathForURL:(CDVFilesystemURL *)url
-{
-    NSString *path = nil;
-    NSString *fullPath = url.fullPath;
-    path = [NSString stringWithFormat:@"%@%@", self.fsRoot, fullPath];
-    if ([path hasSuffix:@"/"]) {
-      path = [path substringToIndex:([path length]-1)];
-    }
-    return path;
-}
-
-
-- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
-{
-    CDVPluginResult* result = nil;
-    BOOL bDirRequest = NO;
-    BOOL create = NO;
-    BOOL exclusive = NO;
-    int errorCode = 0;  // !!! risky - no error code currently defined for 0
-
-    if ([options valueForKeyIsNumber:@"create"]) {
-        create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
-    }
-    if ([options valueForKeyIsNumber:@"exclusive"]) {
-        exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
-    }
-
-    if ([options valueForKeyIsNumber:@"getDir"]) {
-        // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method
-        bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue];
-    }
-    // see if the requested path has invalid characters - should we be checking for  more than just ":"?
-    if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
-        errorCode = ENCODING_ERR;
-    } else {
-        CDVFilesystemURL* requestedURL = [CDVFilesystemURL fileSystemURLWithURL:[NSURL URLWithString:[requestedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] relativeToURL:baseURI.url]]; /* TODO: UGLY - FIX */
-
-        // NSLog(@"reqFullPath = %@", reqFullPath);
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        BOOL bIsDir;
-        BOOL bExists = [fileMgr fileExistsAtPath:[self fileSystemPathForURL:requestedURL] isDirectory:&bIsDir];
-        if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
-            // path exists and is of requested type  - return TYPE_MISMATCH_ERR
-            errorCode = TYPE_MISMATCH_ERR;
-        } else if (!bExists && (create == NO)) {
-            // path does not exist and create is false - return NOT_FOUND_ERR
-            errorCode = NOT_FOUND_ERR;
-        } else if (bExists && (create == YES) && (exclusive == YES)) {
-            // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR
-            errorCode = PATH_EXISTS_ERR;
-        } else {
-            // if bExists and create == YES - just return data
-            // if bExists and create == NO  - just return data
-            // if !bExists and create == YES - create and return data
-            BOOL bSuccess = YES;
-            NSError __autoreleasing* pError = nil;
-            if (!bExists && (create == YES)) {
-                if (bDirRequest) {
-                    // create the dir
-                    bSuccess = [fileMgr createDirectoryAtPath:[self fileSystemPathForURL:requestedURL] withIntermediateDirectories:NO attributes:nil error:&pError];
-                } else {
-                    // create the empty file
-                    bSuccess = [fileMgr createFileAtPath:[self fileSystemPathForURL:requestedURL] contents:nil attributes:nil];
-                }
-            }
-            if (!bSuccess) {
-                errorCode = ABORT_ERR;
-                if (pError) {
-                    NSLog(@"error creating directory: %@", [pError localizedDescription]);
-                }
-            } else {
-                // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]);
-                // file existed or was created
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath fileSystem:baseURI.fileSystemType isDirectory:bDirRequest]];
-            }
-        } // are all possible conditions met?
-    }
-
-    if (errorCode > 0) {
-        // create error callback
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
-    }
-    return result;
-
-}
-
-- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
-{
-    CDVPluginResult* result = nil;
-    CDVFilesystemURL *newURI = nil;
-    if ([localURI.fullPath isEqualToString:@""]) {
-        // return self
-        newURI = localURI;
-    } else {
-        newURI = [CDVFilesystemURL fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */
-    }
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    BOOL bIsDir;
-    BOOL bExists = [fileMgr fileExistsAtPath:[self fileSystemPathForURL:newURI] isDirectory:&bIsDir];
-    if (bExists) {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath fileSystem:newURI.fileSystemType isDirectory:bIsDir]];
-    } else {
-        // invalid path or file does not exist
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-    }
-    return result;
-}
-
-- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback
-{
-
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    NSError* __autoreleasing error = nil;
-
-    CDVPluginResult *result;
-    NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:[self  fileSystemPathForURL:url] error:&error];
-
-    if (fileAttribs) {
-        NSDate* modDate = [fileAttribs fileModificationDate];
-        if (modDate) {
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[modDate timeIntervalSince1970] * 1000];
-        }
-    } else {
-        // didn't get fileAttribs
-        CDVFileError errorCode = ABORT_ERR;
-        NSLog(@"error getting metadata: %@", [error localizedDescription]);
-        if ([error code] == NSFileNoSuchFileError) {
-            errorCode = NOT_FOUND_ERR;
-        }
-        // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
-    }
-    if (!result) {
-        // invalid path or file does not exist
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION];
-    }
-    callback(result);
-}
-
-- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
-{
-    BOOL ok = NO;
-
-    NSString* filePath = [self fileSystemPathForURL:localURI];
-    // we only care about this iCloud key for now.
-    // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
-    NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
-    id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
-
-    if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
-        if (IsAtLeastiOSVersion(@"5.1")) {
-            NSURL* url = [NSURL fileURLWithPath:filePath];
-            NSError* __autoreleasing error = nil;
-
-            ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
-        } else { // below 5.1 (deprecated - only really supported in 5.01)
-            u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
-            if (value == 0) { // remove the attribute (allow backup, the default)
-                ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
-            } else { // set the attribute (skip backup)
-                ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
-            }
-        }
-    }
-
-    if (ok) {
-        return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
-    } else {
-        return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
-    }
-}
-
-/* remove the file or directory (recursively)
- * IN:
- * NSString* fullPath - the full path to the file or directory to be removed
- * NSString* callbackId
- * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling
- */
-
-- (CDVPluginResult*)doRemove:(NSString*)fullPath
-{
-    CDVPluginResult* result = nil;
-    BOOL bSuccess = NO;
-    NSError* __autoreleasing pError = nil;
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-
-    @try {
-        bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError];
-        if (bSuccess) {
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
-        } else {
-            // see if we can give a useful error
-            CDVFileError errorCode = ABORT_ERR;
-            NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]);
-            if ([pError code] == NSFileNoSuchFileError) {
-                errorCode = NOT_FOUND_ERR;
-            } else if ([pError code] == NSFileWriteNoPermissionError) {
-                errorCode = NO_MODIFICATION_ALLOWED_ERR;
-            }
-
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
-        }
-    } @catch(NSException* e) {  // NSInvalidArgumentException if path is . or ..
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR];
-    }
-
-    return result;
-}
-
-- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
-{
-    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
-
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    BOOL bIsDir = NO;
-    BOOL bExists = [fileMgr fileExistsAtPath:fileSystemPath isDirectory:&bIsDir];
-    if (!bExists) {
-        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-    }
-    if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fileSystemPath error:nil] count] != 0)) {
-        // dir is not empty
-        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
-    }
-    return [self doRemove:fileSystemPath];
-}
-
-- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
-{
-    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
-    return [self doRemove:fileSystemPath];
-}
-
-/*
- * IN
- *  NSString localURI
- * OUT
- *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
- *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
- *  or if the URL is malformed.
- * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
- */
-- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath
-{
-    if ([fsPath hasPrefix:self.fsRoot]) {
-        return [fsPath substringFromIndex:[self.fsRoot length]];
-    }
-    return nil;
-}
-
-
-- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
-{
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    NSError* __autoreleasing error = nil;
-    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
-
-    NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fileSystemPath error:&error];
-
-    if (contents) {
-        NSMutableArray* entries = [NSMutableArray arrayWithCapacity:1];
-        if ([contents count] > 0) {
-            // create an Entry (as JSON) for each file/dir
-            for (NSString* name in contents) {
-                // see if is dir or file
-                NSString* entryPath = [fileSystemPath stringByAppendingPathComponent:name];
-                BOOL bIsDir = NO;
-                [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir];
-                NSDictionary* entryDict = [self makeEntryForPath:[self fullPathForFileSystemPath:entryPath] fileSystem:localURI.fileSystemType isDirectory:bIsDir];
-                [entries addObject:entryDict];
-            }
-        }
-        return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:entries];
-    } else {
-        // assume not found but could check error for more specific error conditions
-        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-    }
-}
-
-- (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos
-{
-    unsigned long long newPos = 0UL;
-
-    NSFileHandle* file = [NSFileHandle fileHandleForWritingAtPath:filePath];
-
-    if (file) {
-        [file truncateFileAtOffset:(unsigned long long)pos];
-        newPos = [file offsetInFile];
-        [file synchronizeFile];
-        [file closeFile];
-    }
-    return newPos;
-}
-
-- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
-{
-    unsigned long long newPos = [self truncateFile:[self fileSystemPathForURL:localURI] atPosition:pos];
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:newPos];
-}
-
-- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
-{
-    NSString *filePath = [self fileSystemPathForURL:localURL];
-
-    CDVPluginResult* result = nil;
-    CDVFileError errCode = INVALID_MODIFICATION_ERR;
-    int bytesWritten = 0;
-
-    if (filePath) {
-        NSOutputStream* fileStream = [NSOutputStream outputStreamToFileAtPath:filePath append:shouldAppend];
-        if (fileStream) {
-            NSUInteger len = [encData length];
-            [fileStream open];
-
-            bytesWritten = [fileStream write:[encData bytes] maxLength:len];
-
-            [fileStream close];
-            if (bytesWritten > 0) {
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:bytesWritten];
-                // } else {
-                // can probably get more detailed error info via [fileStream streamError]
-                // errCode already set to INVALID_MODIFICATION_ERR;
-                // bytesWritten = 0; // may be set to -1 on error
-            }
-        } // else fileStream not created return INVALID_MODIFICATION_ERR
-    } else {
-        // invalid filePath
-        errCode = NOT_FOUND_ERR;
-    }
-    if (!result) {
-        // was an error
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
-    }
-    return result;
-}
-
-/**
- * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name,
- * or attempted to copy a directory into a directory that it contains directly or indirectly.
- *
- * IN:
- *  NSString* srcDir
- *  NSString* destinationDir
- * OUT:
- *  YES copy/ move is allows
- *  NO move is onto itself
- */
-- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest
-{
-    // This weird test is to determine if we are copying or moving a directory into itself.
-    // Copy /Documents/myDir to /Documents/myDir-backup is okay but
-    // Copy /Documents/myDir to /Documents/myDir/backup not okay
-    BOOL copyOK = YES;
-    NSRange range = [dest rangeOfString:src];
-
-    if (range.location != NSNotFound) {
-        NSRange testRange = {range.length - 1, ([dest length] - range.length)};
-        NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange];
-        if (resultRange.location != NSNotFound) {
-            copyOK = NO;
-        }
-    }
-    return copyOK;
-}
-
-- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
-{
-    NSFileManager *fileMgr = [[NSFileManager alloc] init];
-    NSString *destRootPath = [self fileSystemPathForURL:destURL];
-    BOOL bDestIsDir = NO;
-    BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
-
-    NSString *newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName];
-    NSString *newFullPath = [self fullPathForFileSystemPath:newFileSystemPath];
-
-    BOOL bNewIsDir = NO;
-    BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir];
-
-    CDVPluginResult *result = nil;
-    int errCode = 0;
-
-    if (!bDestExists) {
-        // the destination root does not exist
-        errCode = NOT_FOUND_ERR;
-    }
-
-    else if ([srcFs isKindOfClass:[CDVLocalFilesystem class]]) {
-        /* Same FS, we can shortcut with NSFileManager operations */
-        NSString *srcFullPath = [self fileSystemPathForURL:srcURL];
-
-        BOOL bSrcIsDir = NO;
-        BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
-
-        if (!bSrcExists) {
-            // the source does not exist
-            errCode = NOT_FOUND_ERR;
-        } else if ([newFileSystemPath isEqualToString:srcFullPath]) {
-            // source and destination can not be the same
-            errCode = INVALID_MODIFICATION_ERR;
-        } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) {
-            // can't copy/move dir to file
-            errCode = INVALID_MODIFICATION_ERR;
-        } else { // no errors yet
-            NSError* __autoreleasing error = nil;
-            BOOL bSuccess = NO;
-            if (bCopy) {
-                if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
-                    // can't copy dir into self
-                    errCode = INVALID_MODIFICATION_ERR;
-                } else if (bNewExists) {
-                    // the full destination should NOT already exist if a copy
-                    errCode = PATH_EXISTS_ERR;
-                } else {
-                    bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
-                }
-            } else { // move
-                // iOS requires that destination must not exist before calling moveTo
-                // is W3C INVALID_MODIFICATION_ERR error if destination dir exists and has contents
-                //
-                if (!bSrcIsDir && (bNewExists && bNewIsDir)) {
-                    // can't move a file to directory
-                    errCode = INVALID_MODIFICATION_ERR;
-                } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
-                    // can't move a dir into itself
-                    errCode = INVALID_MODIFICATION_ERR;
-                } else if (bNewExists) {
-                    if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) {
-                        // can't move dir to a dir that is not empty
-                        errCode = INVALID_MODIFICATION_ERR;
-                        newFileSystemPath = nil;  // so we won't try to move
-                    } else {
-                        // remove destination so can perform the moveItemAtPath
-                        bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL];
-                        if (!bSuccess) {
-                            errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
-                            newFileSystemPath = nil;
-                        }
-                    }
-                } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) {
-                    // can't move a directory inside itself or to any child at any depth;
-                    errCode = INVALID_MODIFICATION_ERR;
-                    newFileSystemPath = nil;
-                }
-
-                if (newFileSystemPath != nil) {
-                    bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
-                }
-            }
-            if (bSuccess) {
-                // should verify it is there and of the correct type???
-                NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:srcURL.fileSystemType isDirectory:bSrcIsDir];  // should be the same type as source
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
-            } else {
-                if (error) {
-                    if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) {
-                        errCode = NOT_READABLE_ERR;
-                    } else if ([error code] == NSFileWriteOutOfSpaceError) {
-                        errCode = QUOTA_EXCEEDED_ERR;
-                    } else if ([error code] == NSFileWriteNoPermissionError) {
-                        errCode = NO_MODIFICATION_ALLOWED_ERR;
-                    }
-                }
-            }
-        }
-    } else {
-        // Need to copy the hard way
-        [srcFs readFileAtURL:srcURL start:0 end:-1 callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
-            CDVPluginResult* result = nil;
-            if (data != nil) {
-                BOOL bSuccess = [data writeToFile:newFileSystemPath atomically:YES];
-                if (bSuccess) {
-                    // should verify it is there and of the correct type???
-                    NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:destURL.fileSystemType isDirectory:NO];
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
-                } else {
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ABORT_ERR];
-                }
-            } else {
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
-            }
-            callback(result);
-        }];
-        return; // Async IO; return without callback.
-    }
-    if (result == nil) {
-        if (!errCode) {
-            errCode = INVALID_MODIFICATION_ERR; // Catch-all default
-        }
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
-    }
-    callback(result);
-}
-
-/* helper function to get the mimeType from the file extension
- * IN:
- *	NSString* fullPath - filename (may include path)
- * OUT:
- *	NSString* the mime type as type/subtype.  nil if not able to determine
- */
-+ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
-{
-    NSString* mimeType = nil;
-
-    if (fullPath) {
-        CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
-        if (typeId) {
-            mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
-            if (!mimeType) {
-                // special case for m4a
-                if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
-                    mimeType = @"audio/mp4";
-                } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
-                    mimeType = @"audio/wav";
-                }
-            }
-            CFRelease(typeId);
-        }
-    }
-    return mimeType;
-}
-
-- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
-{
-    NSString *path = [self fileSystemPathForURL:localURL];
-
-    NSString* mimeType = [CDVLocalFilesystem getMimeTypeFromPath:path];
-    if (mimeType == nil) {
-        mimeType = @"*/*";
-    }
-    NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:path];
-    if (start > 0) {
-        [file seekToFileOffset:start];
-    }
-
-    NSData* readData;
-    if (end < 0) {
-        readData = [file readDataToEndOfFile];
-    } else {
-        readData = [file readDataOfLength:(end - start)];
-    }
-    [file closeFile];
-
-    callback(readData, mimeType, readData != nil ? NO_ERROR : NOT_FOUND_ERR);
-}
-
-- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
-{
-    NSString *path = [self fileSystemPathForURL:localURL];
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        BOOL bIsDir = NO;
-        // make sure it exists and is not a directory
-        BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&bIsDir];
-        if (!bExists || bIsDir) {
-            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
-        } else {
-            // create dictionary of file info
-            NSError* __autoreleasing error = nil;
-            NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:path error:&error];
-            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
-            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
-            [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
-            [fileInfo setObject:@"" forKey:@"type"];  // can't easily get the mimetype unless create URL, send request and read response so skipping
-            [fileInfo setObject:[path lastPathComponent] forKey:@"name"];
-            NSDate* modDate = [fileAttrs fileModificationDate];
-            NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
-            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
-            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
-        }
-}
-
-@end
-
-@interface CDVAssetLibraryFilesystem : NSObject<CDVFileSystem> {
-}
-@end
-
-@implementation CDVAssetLibraryFilesystem
-- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url
-{
-    NSDictionary* fileSystem = [self makeEntryForPath:[url.url absoluteString] fileSystem:ASSETS_LIBRARY isDirectory:NO];
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
-}
-
-- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir
-{
-    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
-    NSString* lastPart = [fullPath lastPathComponent];
-    if (isDir && ![fullPath hasSuffix:@"/"]) {
-        fullPath = [fullPath stringByAppendingString:@"/"];
-    }
-    [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
-    [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
-    [dirEntry setObject:fullPath forKey:@"fullPath"];
-    [dirEntry setObject:lastPart forKey:@"name"];
-    [dirEntry setObject: [NSNumber numberWithInt:fsType] forKey: @"filesystem"];
-
-    return dirEntry;
-}
-
-- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
-{
-    // return unsupported result for assets-library URLs
-   return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
-}
-
-- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
-{
-    // we don't (yet?) support getting the parent of an asset
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
-}
-
-- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback
-{
-    __block CDVPluginResult* result = nil;
-
-    // In this case, we need to use an asynchronous method to retrieve the file.
-    // Because of this, we can't just assign to `result` and send it at the end of the method.
-    // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
-    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
-        if (asset) {
-            // We have the asset!  Retrieve the metadata and send it off.
-            NSDate* date = [asset valueForProperty:ALAssetPropertyDate];
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[date timeIntervalSince1970] * 1000];
-            callback(result);
-        } else {
-            // We couldn't find the asset.  Send the appropriate error.
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-            callback(result);
-        }
-    };
-    // TODO(maxw): Consider making this a class variable since it's the same every time.
-    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
-        // Retrieving the asset failed for some reason.  Send the appropriate error.
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
-        callback(result);
-    };
-
-    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
-    [assetsLibrary assetForURL:url.url resultBlock:resultBlock failureBlock:failureBlock];
-    return;
-}
-
-- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
-{
-    // setMetadata doesn't make sense for asset library files
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
-}
-
-- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
-{
-    // return error for assets-library URLs
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
-}
-
-- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
-{
-    // return error for assets-library URLs
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
-}
-
-- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
-{
-    // return unsupported result for assets-library URLs
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
-}
-
-- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
-{
-    // assets-library files can't be truncated
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
-}
-
-- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
-{
-    // text can't be written into assets-library files
-    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
-}
-
-- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
-{
-    // Copying to an assets library file is not doable, since we can't write it.
-    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
-    callback(result);
-}
-
-- (NSString *)fileSystemPathForURL:(CDVFilesystemURL *)url
-{
-    NSString *path = nil;
-    if ([[url.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        path = [url.url path];
-    } else {
-       path = url.fullPath;
-    }
-    if ([path hasSuffix:@"/"]) {
-      path = [path substringToIndex:([path length]-1)];
-    }
-    return path;
-}
-
-- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
-{
-    NSString *path = [self fileSystemPathForURL:localURL];
-
-    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
-        if (asset) {
-            // We have the asset!  Get the data and send it off.
-            ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
-            Byte* buffer = (Byte*)malloc([assetRepresentation size]);
-            NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
-            NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
-            NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
-
-            callback(data, MIMEType, NO_ERROR);
-        } else {
-            callback(nil, nil, NOT_FOUND_ERR);
-        }
-    };
-
-    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
-        // Retrieving the asset failed for some reason.  Send the appropriate error.
-        NSLog(@"Error: %@", error);
-        callback(nil, nil, SECURITY_ERR);
-    };
-
-    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
-    [assetsLibrary assetForURL:[NSURL URLWithString:path] resultBlock:resultBlock failureBlock:failureBlock];
-}
-
-- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
-{
-    NSString *path = [self fileSystemPathForURL:localURL];
-
-    // In this case, we need to use an asynchronous method to retrieve the file.
-    // Because of this, we can't just assign to `result` and send it at the end of the method.
-    // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
-    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
-        if (asset) {
-            // We have the asset!  Populate the dictionary and send it off.
-            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
-            ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
-            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
-            [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
-            NSString* filename = [assetRepresentation filename];
-            [fileInfo setObject:filename forKey:@"name"];
-            [fileInfo setObject:[CDVLocalFilesystem getMimeTypeFromPath:filename] forKey:@"type"];
-            NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
-            NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
-            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
-
-            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
-        } else {
-            // We couldn't find the asset.  Send the appropriate error.
-            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
-        }
-    };
-    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
-        // Retrieving the asset failed for some reason.  Send the appropriate error.
-        callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]);
-    };
-
-    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
-    [assetsLibrary assetForURL:[NSURL URLWithString:path] resultBlock:resultBlock failureBlock:failureBlock];
-    return;
-}
-@end
-
-@interface CDVFilesystemURLProtocol : NSURLProtocol
-@end
-
 @implementation CDVFilesystemURLProtocol
 
 NSString* const kCDVFilesystemURLPrefix = @"filesystem";

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/7bb39777/src/ios/CDVLocalFilesystem.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVLocalFilesystem.h b/src/ios/CDVLocalFilesystem.h
new file mode 100644
index 0000000..704dad8
--- /dev/null
+++ b/src/ios/CDVLocalFilesystem.h
@@ -0,0 +1,30 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+ */
+
+#import "CDVFile.h"
+
+@interface CDVLocalFilesystem : NSObject<CDVFileSystem> {
+    NSString *_fsRoot;
+}
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot;
+
+@property (nonatomic,strong) NSString *fsRoot;
+
+@end


[3/6] CB-5406: Factor out filesystem providers in iOS

Posted by ia...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/64187ca1/src/ios/CDVFile.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVFile.m b/src/ios/CDVFile.m
index 29a3241..47f32a5 100644
--- a/src/ios/CDVFile.m
+++ b/src/ios/CDVFile.m
@@ -27,51 +27,951 @@
 
 static CDVFile *filePlugin = nil;
 
+extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
+
+#ifndef __IPHONE_5_1
+    NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
+#endif
+
+NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
+NSString* const kCDVAssetsLibraryScheme = @"assets-library";
+
 @interface CDVFilesystemURL : NSObject  {
-    NSURL *url_;
-    CDVFileSystemType fileSystemType_;
-    NSString *fullPath_;
-    NSString *fileSystemPath_;
+    NSURL *_url;
+    CDVFileSystemType _fileSystemType;
+    NSString *_fullPath;
+}
+
+- (id) initWithString:(NSString*)strURL;
+- (id) initWithURL:(NSURL*)URL;
+
+@property (atomic) NSURL *url;
+@property (atomic) CDVFileSystemType fileSystemType;
+@property (atomic) NSString *fullPath;
+
+@end
+
+@implementation CDVFilesystemURL
+@synthesize url=_url;
+@synthesize fileSystemType=_fileSystemType;
+@synthesize fullPath=_fullPath;
+
+- (id) initWithString:(NSString *)strURL
+{
+    if ( self = [super init] ) {
+        NSURL *decodedURL = [NSURL URLWithString:strURL];
+        return [self initWithURL:decodedURL];
+    }
+    return nil;
+}
+
+-(id) initWithURL:(NSURL *)URL
+{
+    if ( self = [super init] ) {
+        _url = URL;
+        _fileSystemType = [self filesystemTypeForLocalURI:URL];
+        _fullPath = [self fullPathForLocalURI:URL];
+    }
+    return self;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  int FileSystem type for this URI, or -1 if it is not recognized.
+ */
+- (CDVFileSystemType)filesystemTypeForLocalURI:(NSURL *)uri
+{
+    if ([[uri scheme] isEqualToString:@"filesystem"] && [[uri host] isEqualToString:@"localhost"]) {
+        if ([[uri path] hasPrefix:@"/temporary"]) {
+            return TEMPORARY;
+        } else if ([[uri path] hasPrefix:@"/persistent"]) {
+            return PERSISTENT;
+        }
+    } else if ([[uri scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+        return ASSETS_LIBRARY;
+    }
+    return -1;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString fullPath component suitable for an Entry object.
+ * The incoming URI should be properly escaped. The returned fullPath is unescaped.
+ */
+- (NSString *)fullPathForLocalURI:(NSURL *)uri
+{
+    int fsType = [self filesystemTypeForLocalURI:uri];
+    if (fsType == TEMPORARY) {
+        return [[uri path] substringFromIndex:10];
+    } else if (fsType == PERSISTENT) {
+        return [[uri path] substringFromIndex:11];
+    }
+    return nil;
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL
+{
+    return [[CDVFilesystemURL alloc] initWithString:strURL];
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL
+{
+    return [[CDVFilesystemURL alloc] initWithURL:URL];
+}
+
+@end
+
+@protocol CDVFileSystem
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options;
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI;
+- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback;
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options;
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI;
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos;
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend;
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback;
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback;
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback;
+@end
+
+@interface CDVLocalFilesystem : NSObject<CDVFileSystem> {
+    NSString *_fsRoot;
+}
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot;
+@property (nonatomic,strong) NSString *fsRoot;
+@end
+
+@implementation CDVLocalFilesystem
+@synthesize fsRoot=_fsRoot;
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot
+{
+    if (self) {
+        _fsRoot = fsRoot;
+    }
+    return self;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the
+ *   URI represents a non-existent path, or is unrecognized or otherwise malformed.
+ */
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
+{
+    CDVPluginResult* result = nil;
+    NSString *path = [self fileSystemPathForURL:url];
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    BOOL isDir = NO;
+    // see if exists and is file or dir
+    BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir];
+    if (bExists) {
+        NSDictionary* fileSystem = [self makeEntryForPath:url.fullPath fileSystem:url.fileSystemType isDirectory:isDir];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+    } else {
+        // return NOT_FOUND_ERR
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    return result;
+}
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir
+{
+    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+    NSString* lastPart = [fullPath lastPathComponent];
+    if (isDir && ![fullPath hasSuffix:@"/"]) {
+        fullPath = [fullPath stringByAppendingString:@"/"];
+    }
+    [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
+    [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
+    [dirEntry setObject:fullPath forKey:@"fullPath"];
+    [dirEntry setObject:lastPart forKey:@"name"];
+    [dirEntry setObject: [NSNumber numberWithInt:fsType] forKey: @"filesystem"];
+
+    return dirEntry;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ *  or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fileSystemPathForURL:(CDVFilesystemURL *)url
+{
+    NSString *path = nil;
+    NSString *fullPath = url.fullPath;
+    path = [NSString stringWithFormat:@"%@%@", self.fsRoot, fullPath];
+    if ([path hasSuffix:@"/"]) {
+      path = [path substringToIndex:([path length]-1)];
+    }
+    return path;
+}
+
+
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
+{
+    CDVPluginResult* result = nil;
+    BOOL bDirRequest = NO;
+    BOOL create = NO;
+    BOOL exclusive = NO;
+    int errorCode = 0;  // !!! risky - no error code currently defined for 0
+
+    if ([options valueForKeyIsNumber:@"create"]) {
+        create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
+    }
+    if ([options valueForKeyIsNumber:@"exclusive"]) {
+        exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
+    }
+
+    if ([options valueForKeyIsNumber:@"getDir"]) {
+        // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method
+        bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue];
+    }
+    // see if the requested path has invalid characters - should we be checking for  more than just ":"?
+    if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
+        errorCode = ENCODING_ERR;
+    } else {
+        CDVFilesystemURL* requestedURL = [CDVFilesystemURL fileSystemURLWithURL:[NSURL URLWithString:[requestedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] relativeToURL:baseURI.url]]; /* TODO: UGLY - FIX */
+
+        // NSLog(@"reqFullPath = %@", reqFullPath);
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir;
+        BOOL bExists = [fileMgr fileExistsAtPath:[self fileSystemPathForURL:requestedURL] isDirectory:&bIsDir];
+        if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
+            // path exists and is of requested type  - return TYPE_MISMATCH_ERR
+            errorCode = TYPE_MISMATCH_ERR;
+        } else if (!bExists && (create == NO)) {
+            // path does not exist and create is false - return NOT_FOUND_ERR
+            errorCode = NOT_FOUND_ERR;
+        } else if (bExists && (create == YES) && (exclusive == YES)) {
+            // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR
+            errorCode = PATH_EXISTS_ERR;
+        } else {
+            // if bExists and create == YES - just return data
+            // if bExists and create == NO  - just return data
+            // if !bExists and create == YES - create and return data
+            BOOL bSuccess = YES;
+            NSError __autoreleasing* pError = nil;
+            if (!bExists && (create == YES)) {
+                if (bDirRequest) {
+                    // create the dir
+                    bSuccess = [fileMgr createDirectoryAtPath:[self fileSystemPathForURL:requestedURL] withIntermediateDirectories:NO attributes:nil error:&pError];
+                } else {
+                    // create the empty file
+                    bSuccess = [fileMgr createFileAtPath:[self fileSystemPathForURL:requestedURL] contents:nil attributes:nil];
+                }
+            }
+            if (!bSuccess) {
+                errorCode = ABORT_ERR;
+                if (pError) {
+                    NSLog(@"error creating directory: %@", [pError localizedDescription]);
+                }
+            } else {
+                // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]);
+                // file existed or was created
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath fileSystem:baseURI.fileSystemType isDirectory:bDirRequest]];
+            }
+        } // are all possible conditions met?
+    }
+
+    if (errorCode > 0) {
+        // create error callback
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+    }
+    return result;
+
+}
+
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
+{
+    CDVPluginResult* result = nil;
+    CDVFilesystemURL *newURI = nil;
+    if ([localURI.fullPath isEqualToString:@""]) {
+        // return self
+        newURI = localURI;
+    } else {
+        newURI = [CDVFilesystemURL fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */
+    }
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    BOOL bIsDir;
+    BOOL bExists = [fileMgr fileExistsAtPath:[self fileSystemPathForURL:newURI] isDirectory:&bIsDir];
+    if (bExists) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath fileSystem:newURI.fileSystemType isDirectory:bIsDir]];
+    } else {
+        // invalid path or file does not exist
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    return result;
+}
+
+- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback
+{
+
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    NSError* __autoreleasing error = nil;
+
+    CDVPluginResult *result;
+    NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:[self  fileSystemPathForURL:url] error:&error];
+
+    if (fileAttribs) {
+        NSDate* modDate = [fileAttribs fileModificationDate];
+        if (modDate) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[modDate timeIntervalSince1970] * 1000];
+        }
+    } else {
+        // didn't get fileAttribs
+        CDVFileError errorCode = ABORT_ERR;
+        NSLog(@"error getting metadata: %@", [error localizedDescription]);
+        if ([error code] == NSFileNoSuchFileError) {
+            errorCode = NOT_FOUND_ERR;
+        }
+        // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
+    }
+    if (!result) {
+        // invalid path or file does not exist
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION];
+    }
+    callback(result);
+}
+
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
+{
+    BOOL ok = NO;
+
+    NSString* filePath = [self fileSystemPathForURL:localURI];
+    // we only care about this iCloud key for now.
+    // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
+    NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
+    id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
+
+    if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
+        if (IsAtLeastiOSVersion(@"5.1")) {
+            NSURL* url = [NSURL fileURLWithPath:filePath];
+            NSError* __autoreleasing error = nil;
+
+            ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
+        } else { // below 5.1 (deprecated - only really supported in 5.01)
+            u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
+            if (value == 0) { // remove the attribute (allow backup, the default)
+                ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
+            } else { // set the attribute (skip backup)
+                ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
+            }
+        }
+    }
+
+    if (ok) {
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+    } else {
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+    }
+}
+
+/* remove the file or directory (recursively)
+ * IN:
+ * NSString* fullPath - the full path to the file or directory to be removed
+ * NSString* callbackId
+ * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling
+ */
+
+- (CDVPluginResult*)doRemove:(NSString*)fullPath
+{
+    CDVPluginResult* result = nil;
+    BOOL bSuccess = NO;
+    NSError* __autoreleasing pError = nil;
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+    @try {
+        bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError];
+        if (bSuccess) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+        } else {
+            // see if we can give a useful error
+            CDVFileError errorCode = ABORT_ERR;
+            NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]);
+            if ([pError code] == NSFileNoSuchFileError) {
+                errorCode = NOT_FOUND_ERR;
+            } else if ([pError code] == NSFileWriteNoPermissionError) {
+                errorCode = NO_MODIFICATION_ALLOWED_ERR;
+            }
+
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+        }
+    } @catch(NSException* e) {  // NSInvalidArgumentException if path is . or ..
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR];
+    }
+
+    return result;
+}
+
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
+{
+    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
+
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    BOOL bIsDir = NO;
+    BOOL bExists = [fileMgr fileExistsAtPath:fileSystemPath isDirectory:&bIsDir];
+    if (!bExists) {
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fileSystemPath error:nil] count] != 0)) {
+        // dir is not empty
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+    }
+    return [self doRemove:fileSystemPath];
+}
+
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
+{
+    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
+    return [self doRemove:fileSystemPath];
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ *  or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath
+{
+    if ([fsPath hasPrefix:self.fsRoot]) {
+        return [fsPath substringFromIndex:[self.fsRoot length]];
+    }
+    return nil;
+}
+
+
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
+{
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    NSError* __autoreleasing error = nil;
+    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
+
+    NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fileSystemPath error:&error];
+
+    if (contents) {
+        NSMutableArray* entries = [NSMutableArray arrayWithCapacity:1];
+        if ([contents count] > 0) {
+            // create an Entry (as JSON) for each file/dir
+            for (NSString* name in contents) {
+                // see if is dir or file
+                NSString* entryPath = [fileSystemPath stringByAppendingPathComponent:name];
+                BOOL bIsDir = NO;
+                [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir];
+                NSDictionary* entryDict = [self makeEntryForPath:[self fullPathForFileSystemPath:entryPath] fileSystem:localURI.fileSystemType isDirectory:bIsDir];
+                [entries addObject:entryDict];
+            }
+        }
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:entries];
+    } else {
+        // assume not found but could check error for more specific error conditions
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+}
+
+- (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos
+{
+    unsigned long long newPos = 0UL;
+
+    NSFileHandle* file = [NSFileHandle fileHandleForWritingAtPath:filePath];
+
+    if (file) {
+        [file truncateFileAtOffset:(unsigned long long)pos];
+        newPos = [file offsetInFile];
+        [file synchronizeFile];
+        [file closeFile];
+    }
+    return newPos;
+}
+
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
+{
+    unsigned long long newPos = [self truncateFile:[self fileSystemPathForURL:localURI] atPosition:pos];
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:newPos];
+}
+
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
+{
+    NSString *filePath = [self fileSystemPathForURL:localURL];
+
+    CDVPluginResult* result = nil;
+    CDVFileError errCode = INVALID_MODIFICATION_ERR;
+    int bytesWritten = 0;
+
+    if (filePath) {
+        NSOutputStream* fileStream = [NSOutputStream outputStreamToFileAtPath:filePath append:shouldAppend];
+        if (fileStream) {
+            NSUInteger len = [encData length];
+            [fileStream open];
+
+            bytesWritten = [fileStream write:[encData bytes] maxLength:len];
+
+            [fileStream close];
+            if (bytesWritten > 0) {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:bytesWritten];
+                // } else {
+                // can probably get more detailed error info via [fileStream streamError]
+                // errCode already set to INVALID_MODIFICATION_ERR;
+                // bytesWritten = 0; // may be set to -1 on error
+            }
+        } // else fileStream not created return INVALID_MODIFICATION_ERR
+    } else {
+        // invalid filePath
+        errCode = NOT_FOUND_ERR;
+    }
+    if (!result) {
+        // was an error
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+    }
+    return result;
+}
+
+/**
+ * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name,
+ * or attempted to copy a directory into a directory that it contains directly or indirectly.
+ *
+ * IN:
+ *  NSString* srcDir
+ *  NSString* destinationDir
+ * OUT:
+ *  YES copy/ move is allows
+ *  NO move is onto itself
+ */
+- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest
+{
+    // This weird test is to determine if we are copying or moving a directory into itself.
+    // Copy /Documents/myDir to /Documents/myDir-backup is okay but
+    // Copy /Documents/myDir to /Documents/myDir/backup not okay
+    BOOL copyOK = YES;
+    NSRange range = [dest rangeOfString:src];
+
+    if (range.location != NSNotFound) {
+        NSRange testRange = {range.length - 1, ([dest length] - range.length)};
+        NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange];
+        if (resultRange.location != NSNotFound) {
+            copyOK = NO;
+        }
+    }
+    return copyOK;
+}
+
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
+{
+    NSFileManager *fileMgr = [[NSFileManager alloc] init];
+    NSString *destRootPath = [self fileSystemPathForURL:destURL];
+    BOOL bDestIsDir = NO;
+    BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
+
+    NSString *newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName];
+    NSString *newFullPath = [self fullPathForFileSystemPath:newFileSystemPath];
+
+    BOOL bNewIsDir = NO;
+    BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir];
+
+    CDVPluginResult *result = nil;
+    int errCode = 0;
+
+    if (!bDestExists) {
+        // the destination root does not exist
+        errCode = NOT_FOUND_ERR;
+    }
+
+    else if ([srcFs isKindOfClass:[CDVLocalFilesystem class]]) {
+        /* Same FS, we can shortcut with NSFileManager operations */
+        NSString *srcFullPath = [self fileSystemPathForURL:srcURL];
+
+        BOOL bSrcIsDir = NO;
+        BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
+
+        if (!bSrcExists) {
+            // the source does not exist
+            errCode = NOT_FOUND_ERR;
+        } else if ([newFileSystemPath isEqualToString:srcFullPath]) {
+            // source and destination can not be the same
+            errCode = INVALID_MODIFICATION_ERR;
+        } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) {
+            // can't copy/move dir to file
+            errCode = INVALID_MODIFICATION_ERR;
+        } else { // no errors yet
+            NSError* __autoreleasing error = nil;
+            BOOL bSuccess = NO;
+            if (bCopy) {
+                if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+                    // can't copy dir into self
+                    errCode = INVALID_MODIFICATION_ERR;
+                } else if (bNewExists) {
+                    // the full destination should NOT already exist if a copy
+                    errCode = PATH_EXISTS_ERR;
+                } else {
+                    bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+                }
+            } else { // move
+                // iOS requires that destination must not exist before calling moveTo
+                // is W3C INVALID_MODIFICATION_ERR error if destination dir exists and has contents
+                //
+                if (!bSrcIsDir && (bNewExists && bNewIsDir)) {
+                    // can't move a file to directory
+                    errCode = INVALID_MODIFICATION_ERR;
+                } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+                    // can't move a dir into itself
+                    errCode = INVALID_MODIFICATION_ERR;
+                } else if (bNewExists) {
+                    if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) {
+                        // can't move dir to a dir that is not empty
+                        errCode = INVALID_MODIFICATION_ERR;
+                        newFileSystemPath = nil;  // so we won't try to move
+                    } else {
+                        // remove destination so can perform the moveItemAtPath
+                        bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL];
+                        if (!bSuccess) {
+                            errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
+                            newFileSystemPath = nil;
+                        }
+                    }
+                } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) {
+                    // can't move a directory inside itself or to any child at any depth;
+                    errCode = INVALID_MODIFICATION_ERR;
+                    newFileSystemPath = nil;
+                }
+
+                if (newFileSystemPath != nil) {
+                    bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+                }
+            }
+            if (bSuccess) {
+                // should verify it is there and of the correct type???
+                NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:srcURL.fileSystemType isDirectory:bSrcIsDir];  // should be the same type as source
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+            } else {
+                if (error) {
+                    if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) {
+                        errCode = NOT_READABLE_ERR;
+                    } else if ([error code] == NSFileWriteOutOfSpaceError) {
+                        errCode = QUOTA_EXCEEDED_ERR;
+                    } else if ([error code] == NSFileWriteNoPermissionError) {
+                        errCode = NO_MODIFICATION_ALLOWED_ERR;
+                    }
+                }
+            }
+        }
+    } else {
+        // Need to copy the hard way
+        [srcFs readFileAtURL:srcURL start:0 end:-1 callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+            CDVPluginResult* result = nil;
+            if (data != nil) {
+                BOOL bSuccess = [data writeToFile:newFileSystemPath atomically:YES];
+                if (bSuccess) {
+                    // should verify it is there and of the correct type???
+                    NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:destURL.fileSystemType isDirectory:NO];
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+                } else {
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ABORT_ERR];
+                }
+            } else {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+            }
+            callback(result);
+        }];
+        return; // Async IO; return without callback.
+    }
+    if (result == nil) {
+        if (!errCode) {
+            errCode = INVALID_MODIFICATION_ERR; // Catch-all default
+        }
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
+    }
+    callback(result);
 }
 
-- (id) initWithString:(NSString*)strURL;
-- (id) initWithURL:(NSURL*)URL;
+/* helper function to get the mimeType from the file extension
+ * IN:
+ *	NSString* fullPath - filename (may include path)
+ * OUT:
+ *	NSString* the mime type as type/subtype.  nil if not able to determine
+ */
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+    NSString* mimeType = nil;
 
-@property (atomic) NSURL *url;
-@property (atomic) CDVFileSystemType fileSystemType;
-@property (atomic) NSString *fullPath;
-@property (atomic) NSString *fileSystemPath;
+    if (fullPath) {
+        CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
+        if (typeId) {
+            mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
+            if (!mimeType) {
+                // special case for m4a
+                if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
+                    mimeType = @"audio/mp4";
+                } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
+                    mimeType = @"audio/wav";
+                }
+            }
+            CFRelease(typeId);
+        }
+    }
+    return mimeType;
+}
+
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+
+    NSString* mimeType = [CDVLocalFilesystem getMimeTypeFromPath:path];
+    if (mimeType == nil) {
+        mimeType = @"*/*";
+    }
+    NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:path];
+    if (start > 0) {
+        [file seekToFileOffset:start];
+    }
+
+    NSData* readData;
+    if (end < 0) {
+        readData = [file readDataToEndOfFile];
+    } else {
+        readData = [file readDataOfLength:(end - start)];
+    }
+    [file closeFile];
+
+    callback(readData, mimeType, readData != nil ? NO_ERROR : NOT_FOUND_ERR);
+}
+
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir = NO;
+        // make sure it exists and is not a directory
+        BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&bIsDir];
+        if (!bExists || bIsDir) {
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
+        } else {
+            // create dictionary of file info
+            NSError* __autoreleasing error = nil;
+            NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:path error:&error];
+            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
+            [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
+            [fileInfo setObject:@"" forKey:@"type"];  // can't easily get the mimetype unless create URL, send request and read response so skipping
+            [fileInfo setObject:[path lastPathComponent] forKey:@"name"];
+            NSDate* modDate = [fileAttrs fileModificationDate];
+            NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
+            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
+        }
+}
 
 @end
 
-@implementation CDVFilesystemURL
-@synthesize url=url_;
-@synthesize fileSystemType=fileSystemType_;
-@synthesize fullPath=fullPath_, fileSystemPath=fileSystemPath_;
+@interface CDVAssetLibraryFilesystem : NSObject<CDVFileSystem> {
+}
+@end
 
-- (id) initWithString:(NSString *)strURL
+@implementation CDVAssetLibraryFilesystem
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url
 {
-  NSURL *decodedURL = [NSURL URLWithString:strURL];
-  return [self initWithURL:decodedURL];
+    NSDictionary* fileSystem = [self makeEntryForPath:[url.url absoluteString] fileSystem:ASSETS_LIBRARY isDirectory:NO];
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
 }
 
--(id) initWithURL:(NSURL *)URL
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir
 {
-  url_ = URL;
-  fileSystemType_ = -42;
-  return self;
+    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+    NSString* lastPart = [fullPath lastPathComponent];
+    if (isDir && ![fullPath hasSuffix:@"/"]) {
+        fullPath = [fullPath stringByAppendingString:@"/"];
+    }
+    [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
+    [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
+    [dirEntry setObject:fullPath forKey:@"fullPath"];
+    [dirEntry setObject:lastPart forKey:@"name"];
+    [dirEntry setObject: [NSNumber numberWithInt:fsType] forKey: @"filesystem"];
+
+    return dirEntry;
 }
 
-@end
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
+{
+    // return unsupported result for assets-library URLs
+   return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
+}
 
-extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
+{
+    // we don't (yet?) support getting the parent of an asset
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
+}
 
-#ifndef __IPHONE_5_1
-    NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
-#endif
+- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback
+{
+    __block CDVPluginResult* result = nil;
 
-NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
-NSString* const kCDVAssetsLibraryScheme = @"assets-library";
+    // In this case, we need to use an asynchronous method to retrieve the file.
+    // Because of this, we can't just assign to `result` and send it at the end of the method.
+    // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+        if (asset) {
+            // We have the asset!  Retrieve the metadata and send it off.
+            NSDate* date = [asset valueForProperty:ALAssetPropertyDate];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[date timeIntervalSince1970] * 1000];
+            callback(result);
+        } else {
+            // We couldn't find the asset.  Send the appropriate error.
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+            callback(result);
+        }
+    };
+    // TODO(maxw): Consider making this a class variable since it's the same every time.
+    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+        // Retrieving the asset failed for some reason.  Send the appropriate error.
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+        callback(result);
+    };
+
+    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+    [assetsLibrary assetForURL:url.url resultBlock:resultBlock failureBlock:failureBlock];
+    return;
+}
+
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
+{
+    // setMetadata doesn't make sense for asset library files
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+}
+
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
+{
+    // return error for assets-library URLs
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+}
+
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
+{
+    // return error for assets-library URLs
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
+{
+    // return unsupported result for assets-library URLs
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
+}
+
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
+{
+    // assets-library files can't be truncated
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+}
+
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
+{
+    // text can't be written into assets-library files
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+}
+
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
+{
+    // Copying to an assets library file is not doable, since we can't write it.
+    CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+    callback(result);
+}
+
+- (NSString *)fileSystemPathForURL:(CDVFilesystemURL *)url
+{
+    NSString *path = nil;
+    if ([[url.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+        path = [url.url path];
+    } else {
+       path = url.fullPath;
+    }
+    if ([path hasSuffix:@"/"]) {
+      path = [path substringToIndex:([path length]-1)];
+    }
+    return path;
+}
+
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+
+    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+        if (asset) {
+            // We have the asset!  Get the data and send it off.
+            ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+            Byte* buffer = (Byte*)malloc([assetRepresentation size]);
+            NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
+            NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+            NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
+
+            callback(data, MIMEType, NO_ERROR);
+        } else {
+            callback(nil, nil, NOT_FOUND_ERR);
+        }
+    };
+
+    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+        // Retrieving the asset failed for some reason.  Send the appropriate error.
+        NSLog(@"Error: %@", error);
+        callback(nil, nil, SECURITY_ERR);
+    };
+
+    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+    [assetsLibrary assetForURL:[NSURL URLWithString:path] resultBlock:resultBlock failureBlock:failureBlock];
+}
+
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+
+    // In this case, we need to use an asynchronous method to retrieve the file.
+    // Because of this, we can't just assign to `result` and send it at the end of the method.
+    // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+    ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+        if (asset) {
+            // We have the asset!  Populate the dictionary and send it off.
+            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+            ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
+            [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
+            NSString* filename = [assetRepresentation filename];
+            [fileInfo setObject:filename forKey:@"name"];
+            [fileInfo setObject:[CDVLocalFilesystem getMimeTypeFromPath:filename] forKey:@"type"];
+            NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
+            NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
+            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
+        } else {
+            // We couldn't find the asset.  Send the appropriate error.
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
+        }
+    };
+    ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+        // Retrieving the asset failed for some reason.  Send the appropriate error.
+        callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]]);
+    };
+
+    ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+    [assetsLibrary assetForURL:[NSURL URLWithString:path] resultBlock:resultBlock failureBlock:failureBlock];
+    return;
+}
+@end
 
 @interface CDVFilesystemURLProtocol : NSURLProtocol
 @end
@@ -98,17 +998,16 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 
 - (void)startLoading
 {
-    NSURL *url = [[self request] URL];
-    NSString *pathString = [url relativePath];
-    pathString = [filePlugin fileSystemPathForLocalURI:url];
-    [CDVFile readFileWithPath:pathString start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) {
+    CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithURL:[[self request] URL]];
+    CDVLocalFilesystem *fs = [filePlugin.fileSystems objectAtIndex:url.fileSystemType];
+    [fs readFileAtURL:url start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) {
         if (!error) {
-            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:@{@"Content-Type": mimetype}];
+            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:@{@"Content-Type": mimetype}];
             [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
             [[self client] URLProtocol:self didLoadData:data];
             [[self client] URLProtocolDidFinishLoading:self];
         } else {
-            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:@{}];
+            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:@{}];
             [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
             [[self client] URLProtocolDidFinishLoading:self];
         }
@@ -125,7 +1024,7 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 
 @implementation CDVFile
 
-@synthesize appDocsPath, appLibraryPath, appTempPath, persistentPath, temporaryPath, userHasAllowed;
+@synthesize appDocsPath, appLibraryPath, appTempPath, persistentPath, temporaryPath, userHasAllowed, fileSystems=fileSystems_;
 
 - (id)initWithWebView:(UIWebView*)theWebView
 {
@@ -134,15 +1033,22 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
         filePlugin = self;
         [NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]];
 
-        // get the documents directory path
-        NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
-        self.appDocsPath = [paths objectAtIndex:0];
+        fileSystems_ = [[NSMutableArray alloc] initWithCapacity:3];
 
-        paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+        // get the temporary directory path
+        NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
         self.appLibraryPath = [paths objectAtIndex:0];
 
         self.appTempPath = [NSTemporaryDirectory()stringByStandardizingPath];   // remove trailing slash from NSTemporaryDirectory()
 
+        [fileSystems_ addObject:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:[paths objectAtIndex:0]]];
+
+        // get the documents directory path
+        paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+        self.appDocsPath = [paths objectAtIndex:0];
+
+        [fileSystems_ addObject:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:[paths objectAtIndex:0]]];
+
         self.persistentPath = [NSString stringWithFormat:@"/%@", [self.appDocsPath lastPathComponent]];
         self.temporaryPath = [NSString stringWithFormat:@"/%@", [self.appTempPath lastPathComponent]];
         // NSLog(@"docs: %@ - temp: %@", self.appDocsPath, self.appTempPath);
@@ -151,115 +1057,6 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
     return self;
 }
 
-- (void)updateFileSystemURL:(CDVFilesystemURL *)fsURL;
-{
-    fsURL.fileSystemType = [self filesystemTypeForLocalURI:fsURL.url];
-    fsURL.fileSystemPath = [self fileSystemPathForLocalURI:fsURL.url];
-    fsURL.fullPath = [self fullPathForFileSystemPath:fsURL.fileSystemPath];
-}
-    
-- (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL
-{
-    CDVFilesystemURL* fsURL = [[CDVFilesystemURL alloc] initWithString:strURL];
-    if (fsURL) {
-        [self updateFileSystemURL:fsURL];
-    }
-    return fsURL;
-}
-    
-- (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL
-{
-    CDVFilesystemURL* fsURL = [[CDVFilesystemURL alloc] initWithURL:URL];
-    if (fsURL) {
-        [self updateFileSystemURL:fsURL];
-    }
-    return fsURL;
-}
-    
-/*
- * IN
- *  NSString localURI
- * OUT
- *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
- *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
- *  or if the URL is malformed.
- * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
- */
-- (NSString *)fileSystemPathForLocalURI:(NSURL *)uri
-{
-    NSString *path = nil;
-    int fsType = [self filesystemTypeForLocalURI:uri];
-    NSString *fullPath = [self fullPathForLocalURI:uri];
-    if (fsType == TEMPORARY) {
-        path = [NSString stringWithFormat:@"%@%@", self.appTempPath, fullPath];
-    } else if (fsType == PERSISTENT) {
-        path = [NSString stringWithFormat:@"%@%@", self.appDocsPath, fullPath];
-    } else {
-        return nil;
-    }
-    if ([path hasSuffix:@"/"]) {
-      path = [path substringToIndex:([path length]-1)];
-    }
-
-    return path;
-}
-
-/*
- * IN
- *  NSString localURI
- * OUT
- *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
- *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
- *  or if the URL is malformed.
- * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
- */
-- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath
-{
-    if ([fsPath hasPrefix:self.appTempPath]) {
-        return [fsPath substringFromIndex:[self.appTempPath length]];
-    } else if ([fsPath hasPrefix:self.appDocsPath]) {
-        return [fsPath substringFromIndex:[self.appDocsPath length]];
-    }
-    return nil;
-}
-
-
-/*
- * IN
- *  NSString localURI
- * OUT
- *  NSString fullPath component suitable for an Entry object.
- * The incoming URI should be properly escaped. The returned fullPath is unescaped.
- */
-- (NSString *)fullPathForLocalURI:(NSURL *)uri
-{
-    int fsType = [self filesystemTypeForLocalURI:uri];
-    if (fsType == TEMPORARY) {
-        return [[uri path] substringFromIndex:10];
-    } else if (fsType == PERSISTENT) {
-        return [[uri path] substringFromIndex:11];
-    }
-    return nil;
-}
-
-/*
- * IN
- *  NSString localURI
- * OUT
- *  int FileSystem type for this URI, or -1 if it is not recognized.
- */
-- (CDVFileSystemType)filesystemTypeForLocalURI:(NSURL *)uri
-{
-    if ([[uri scheme] isEqualToString:@"filesystem"] && [[uri host] isEqualToString:@"localhost"]) {
-        if ([[uri path] hasPrefix:@"/temporary"]) {
-            return TEMPORARY;
-        } else if ([[uri path] hasPrefix:@"/persistent"]) {
-            return PERSISTENT;
-        }
-    }
-    return -1;
-}
-
 
 - (NSNumber*)checkFreeDiskSpace:(NSString*)appPath
 {
@@ -269,32 +1066,8 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 
     NSDictionary* pDict = [fMgr attributesOfFileSystemForPath:appPath error:&pError];
     NSNumber* pNumAvail = (NSNumber*)[pDict objectForKey:NSFileSystemFreeSize];
-
-    return pNumAvail;
-}
-
-// figure out if the (device local file sytstem) pathFragment represents a persistent or temporary directory
-// and return the full application path.
-// returns nil if path is not persistent or temporary
-- (NSString*)getAppPath:(NSString*)pathFragment
-{
-    NSString* appPath = nil;
-    NSRange rangeP = [pathFragment rangeOfString:self.persistentPath];
-    NSRange rangeT = [pathFragment rangeOfString:self.temporaryPath];
-
-    if ((rangeP.location != NSNotFound) && (rangeT.location != NSNotFound)) {
-        // we found both in the path, return whichever one is first
-        if (rangeP.length < rangeT.length) {
-            appPath = self.appDocsPath;
-        } else {
-            appPath = self.appTempPath;
-        }
-    } else if (rangeP.location != NSNotFound) {
-        appPath = self.appDocsPath;
-    } else if (rangeT.location != NSNotFound) {
-        appPath = self.appTempPath;
-    }
-    return appPath;
+
+    return pNumAvail;
 }
 
 /* Request the File System info
@@ -378,12 +1151,6 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
     return dirEntry;
 }
 
-/* STRONGLY DEPRECATED */
-- (NSDictionary*)getDirectoryEntry:(NSString*)fullPath isDirectory:(BOOL)isDir
-{
-  return [self makeEntryForPath:fullPath fileSystem:42 isDirectory:isDir];
-}
-
 /*
  * Given a URI determine the File System information associated with it and return an appropriate W3C entry object
  * IN
@@ -399,46 +1166,18 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 - (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    CDVFilesystemURL* inputUri = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
-    CDVPluginResult* result = [self entryForLocalURI:inputUri];
-    if (result != nil) {
-        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-    }
-}
-
-/*
- * IN
- *  NSString localURI
- * OUT
- *  CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the
- *   URI represents a non-existent path, or is unrecognized or otherwise malformed.
- */
-- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
-{
-    CDVPluginResult* result = nil;
-    if ([[url.url scheme] isEqualToString:@"assets-library"]) {
-        NSDictionary* fileSystem = [self getDirectoryEntry:[url.url absoluteString] isDirectory:NO];
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+    CDVFilesystemURL* inputURI = [CDVFilesystemURL fileSystemURLWithString:[command.arguments objectAtIndex:0]];
+    CDVPluginResult* result;
+    if (inputURI.fileSystemType == -1) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
     } else {
-        if (url.fileSystemType == -1) {
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
-        } else {
-            NSFileManager* fileMgr = [[NSFileManager alloc] init];
-            BOOL isDir = NO;
-            // see if exists and is file or dir
-            BOOL bExists = [fileMgr fileExistsAtPath:url.fileSystemPath isDirectory:&isDir];
-            if (bExists) {
-                NSDictionary* fileSystem = [self makeEntryForPath:url.fullPath fileSystem:url.fileSystemType isDirectory:isDir];
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
-            } else {
-                // return NOT_FOUND_ERR
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-            }
-        }
+        CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:inputURI.fileSystemType];
+        result = [fs entryForLocalURI:inputURI];
     }
-    return result;
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
+
 /* Part of DirectoryEntry interface,  creates or returns the specified directory
  * IN:
  *	NSString* localURI - local filesystem URI for this directory
@@ -499,85 +1238,13 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 - (void)getFile:(CDVInvokedUrlCommand*)command
 {
     NSString* baseURIstr = [command.arguments objectAtIndex:0];
-    CDVFilesystemURL* baseURI = [self fileSystemURLWithString:baseURIstr];
+    CDVFilesystemURL* baseURI = [CDVFilesystemURL fileSystemURLWithString:baseURIstr];
     NSString* requestedPath = [command.arguments objectAtIndex:1];
     NSDictionary* options = [command.arguments objectAtIndex:2 withDefault:nil];
 
-    // return unsupported result for assets-library URLs
-    if ([[baseURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
-        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-        return;
-    }
-
-    CDVPluginResult* result = nil;
-    BOOL bDirRequest = NO;
-    BOOL create = NO;
-    BOOL exclusive = NO;
-    int errorCode = 0;  // !!! risky - no error code currently defined for 0
-
-    if ([options valueForKeyIsNumber:@"create"]) {
-        create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
-    }
-    if ([options valueForKeyIsNumber:@"exclusive"]) {
-        exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
-    }
-
-    if ([options valueForKeyIsNumber:@"getDir"]) {
-        // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method
-        bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue];
-    }
-    // see if the requested path has invalid characters - should we be checking for  more than just ":"?
-    if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
-        errorCode = ENCODING_ERR;
-    } else {
-        CDVFilesystemURL* requestedURL = [self fileSystemURLWithURL:[NSURL URLWithString:[requestedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] relativeToURL:baseURI.url]]; /* TODO: UGLY - FIX */
-        
-        // NSLog(@"reqFullPath = %@", reqFullPath);
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        BOOL bIsDir;
-        BOOL bExists = [fileMgr fileExistsAtPath:requestedURL.fileSystemPath isDirectory:&bIsDir];
-        if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
-            // path exists and is of requested type  - return TYPE_MISMATCH_ERR
-            errorCode = TYPE_MISMATCH_ERR;
-        } else if (!bExists && (create == NO)) {
-            // path does not exist and create is false - return NOT_FOUND_ERR
-            errorCode = NOT_FOUND_ERR;
-        } else if (bExists && (create == YES) && (exclusive == YES)) {
-            // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR
-            errorCode = PATH_EXISTS_ERR;
-        } else {
-            // if bExists and create == YES - just return data
-            // if bExists and create == NO  - just return data
-            // if !bExists and create == YES - create and return data
-            BOOL bSuccess = YES;
-            NSError __autoreleasing* pError = nil;
-            if (!bExists && (create == YES)) {
-                if (bDirRequest) {
-                    // create the dir
-                    bSuccess = [fileMgr createDirectoryAtPath:requestedURL.fileSystemPath withIntermediateDirectories:NO attributes:nil error:&pError];
-                } else {
-                    // create the empty file
-                    bSuccess = [fileMgr createFileAtPath:requestedURL.fileSystemPath contents:nil attributes:nil];
-                }
-            }
-            if (!bSuccess) {
-                errorCode = ABORT_ERR;
-                if (pError) {
-                    NSLog(@"error creating directory: %@", [pError localizedDescription]);
-                }
-            } else {
-                // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]);
-                // file existed or was created
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath fileSystem:baseURI.fileSystemType isDirectory:bDirRequest]];
-            }
-        } // are all possible conditions met?
-    }
+    CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:baseURI.fileSystemType];
+    CDVPluginResult* result = [fs getFileForURL:baseURI requestedPath:requestedPath options:options];
 
-    if (errorCode > 0) {
-        // create error callback
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
-    }
 
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
@@ -594,32 +1261,11 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 - (void)getParent:(CDVInvokedUrlCommand*)command
 {
     // arguments are URL encoded
-    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
+    CDVFilesystemURL* localURI = [CDVFilesystemURL fileSystemURLWithString:[command.arguments objectAtIndex:0]];
 
-    // we don't (yet?) support getting the parent of an asset
-    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
-        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-        return;
-    }
+    CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:localURI.fileSystemType];
+    CDVPluginResult* result = [fs getParentForURL:localURI];
 
-    CDVPluginResult* result = nil;
-    CDVFilesystemURL *newURI = nil;
-    if ([localURI.fullPath isEqualToString:@""]) {
-        // return self
-        newURI = localURI;
-    } else {
-        newURI = [self fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */
-    }
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    BOOL bIsDir;
-    BOOL bExists = [fileMgr fileExistsAtPath:newURI.fileSystemPath isDirectory:&bIsDir];
-    if (bExists) {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath fileSystem:newURI.fileSystemType isDirectory:bIsDir]];
-    } else {
-        // invalid path or file does not exist
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-    }
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
@@ -631,63 +1277,12 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 {
     // arguments
     NSString* localURIstr = [command.arguments objectAtIndex:0];
-    CDVFilesystemURL* localURI = [self fileSystemURLWithString:localURIstr];
-    
-    __block CDVPluginResult* result = nil;
-
-    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        // In this case, we need to use an asynchronous method to retrieve the file.
-        // Because of this, we can't just assign to `result` and send it at the end of the method.
-        // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
-        ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
-            if (asset) {
-                // We have the asset!  Retrieve the metadata and send it off.
-                NSDate* date = [asset valueForProperty:ALAssetPropertyDate];
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[date timeIntervalSince1970] * 1000];
-                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-            } else {
-                // We couldn't find the asset.  Send the appropriate error.
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-            }
-        };
-        // TODO(maxw): Consider making this a class variable since it's the same every time.
-        ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
-            // Retrieving the asset failed for some reason.  Send the appropriate error.
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
-            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-        };
-
-        ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
-        [assetsLibrary assetForURL:[NSURL URLWithString:localURIstr] resultBlock:resultBlock failureBlock:failureBlock];
-        return;
-    }
-
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-    NSError* __autoreleasing error = nil;
-
-    NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:localURI.fileSystemPath error:&error];
+    CDVFilesystemURL* localURI = [CDVFilesystemURL fileSystemURLWithString:localURIstr];
+    CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:localURI.fileSystemType];
+    [fs getMetadataForURL:localURI callback:^(CDVPluginResult* result) {
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    }];
 
-    if (fileAttribs) {
-        NSDate* modDate = [fileAttribs fileModificationDate];
-        if (modDate) {
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[modDate timeIntervalSince1970] * 1000];
-        }
-    } else {
-        // didn't get fileAttribs
-        CDVFileError errorCode = ABORT_ERR;
-        NSLog(@"error getting metadata: %@", [error localizedDescription]);
-        if ([error code] == NSFileNoSuchFileError) {
-            errorCode = NOT_FOUND_ERR;
-        }
-        // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
-    }
-    if (!result) {
-        // invalid path or file does not exist
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION];
-    }
-    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
 /*
@@ -697,42 +1292,11 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 - (void)setMetadata:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
+    CDVFilesystemURL* localURI = [CDVFilesystemURL fileSystemURLWithString:[command.arguments objectAtIndex:0]];
     NSDictionary* options = [command.arguments objectAtIndex:1 withDefault:nil];
+    CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:localURI.fileSystemType];
+    CDVPluginResult* result = [fs setMetadataForURL:localURI withObject:options];
 
-    CDVPluginResult* result = nil;
-    BOOL ok = NO;
-
-    // setMetadata doesn't make sense for asset library files
-    if (![[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        NSString* filePath = localURI.fileSystemPath;
-        // we only care about this iCloud key for now.
-        // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
-        NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
-        id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
-
-        if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
-            if (IsAtLeastiOSVersion(@"5.1")) {
-                NSURL* url = [NSURL fileURLWithPath:filePath];
-                NSError* __autoreleasing error = nil;
-
-                ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
-            } else { // below 5.1 (deprecated - only really supported in 5.01)
-                u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
-                if (value == 0) { // remove the attribute (allow backup, the default)
-                    ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
-                } else { // set the attribute (skip backup)
-                    ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
-                }
-            }
-        }
-    }
-
-    if (ok) {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
-    } else {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
-    }
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
@@ -748,33 +1312,15 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 - (void)remove:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
+    CDVFilesystemURL* localURI = [CDVFilesystemURL fileSystemURLWithString:[command.arguments objectAtIndex:0]];
     CDVPluginResult* result = nil;
-    CDVFileError errorCode = 0;  // !! 0 not currently defined
 
-    // return error for assets-library URLs
-    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        errorCode = INVALID_MODIFICATION_ERR;
-    } else if ([localURI.fullPath isEqualToString:@""]) {
+    if ([localURI.fullPath isEqualToString:@""]) {
         // error if try to remove top level (documents or tmp) dir
-        errorCode = NO_MODIFICATION_ALLOWED_ERR;
-    } else {
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        BOOL bIsDir = NO;
-        BOOL bExists = [fileMgr fileExistsAtPath:localURI.fileSystemPath isDirectory:&bIsDir];
-        if (!bExists) {
-            errorCode = NOT_FOUND_ERR;
-        }
-        if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:localURI.fileSystemPath error:nil] count] != 0)) {
-            // dir is not empty
-            errorCode = INVALID_MODIFICATION_ERR;
-        }
-    }
-    if (errorCode > 0) {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
     } else {
-        // perform actual remove
-        result = [self doRemove:localURI.fileSystemPath];
+        CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:localURI.fileSystemType];
+        result = [fs removeFileAtURL:localURI];
     }
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
@@ -790,99 +1336,27 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
 - (void)removeRecursively:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
-
-    // return unsupported result for assets-library URLs
-    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
-        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-        return;
-    }
-
+    CDVFilesystemURL* localURI = [CDVFilesystemURL fileSystemURLWithString:[command.arguments objectAtIndex:0]];
     CDVPluginResult* result = nil;
-    // error if try to remove top level (documents or tmp) dir
+
     if ([localURI.fullPath isEqualToString:@""]) {
+        // error if try to remove top level (documents or tmp) dir
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
-    } else {
-        result = [self doRemove:localURI.fileSystemPath];
-    }
-    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-}
-
-/* remove the file or directory (recursively)
- * IN:
- * NSString* fullPath - the full path to the file or directory to be removed
- * NSString* callbackId
- * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling
- */
-
-- (CDVPluginResult*)doRemove:(NSString*)fullPath
-{
-    CDVPluginResult* result = nil;
-    BOOL bSuccess = NO;
-    NSError* __autoreleasing pError = nil;
-    NSFileManager* fileMgr = [[NSFileManager alloc] init];
-
-    @try {
-        bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError];
-        if (bSuccess) {
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
-        } else {
-            // see if we can give a useful error
-            CDVFileError errorCode = ABORT_ERR;
-            NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]);
-            if ([pError code] == NSFileNoSuchFileError) {
-                errorCode = NOT_FOUND_ERR;
-            } else if ([pError code] == NSFileWriteNoPermissionError) {
-                errorCode = NO_MODIFICATION_ALLOWED_ERR;
-            }
-
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
-        }
-    } @catch(NSException* e) {  // NSInvalidArgumentException if path is . or ..
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR];
-    }
-
-    return result;
-}
-
-- (void)copyTo:(CDVInvokedUrlCommand*)command
-{
-    [self doCopyMove:command isCopy:YES];
-}
-
-- (void)moveTo:(CDVInvokedUrlCommand*)command
-{
-    [self doCopyMove:command isCopy:NO];
-}
-
-/**
- * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name,
- * or attempted to copy a directory into a directory that it contains directly or indirectly.
- *
- * IN:
- *  NSString* srcDir
- *  NSString* destinationDir
- * OUT:
- *  YES copy/ move is allows
- *  NO move is onto itself
- */
-- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest
-{
-    // This weird test is to determine if we are copying or moving a directory into itself.
-    // Copy /Documents/myDir to /Documents/myDir-backup is okay but
-    // Copy /Documents/myDir to /Documents/myDir/backup not okay
-    BOOL copyOK = YES;
-    NSRange range = [dest rangeOfString:src];
-
-    if (range.location != NSNotFound) {
-        NSRange testRange = {range.length - 1, ([dest length] - range.length)};
-        NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange];
-        if (resultRange.location != NSNotFound) {
-            copyOK = NO;
-        }
+    } else {
+        CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:localURI.fileSystemType];
+        result = [fs recursiveRemoveFileAtURL:localURI];
     }
-    return copyOK;
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)copyTo:(CDVInvokedUrlCommand*)command
+{
+    [self doCopyMove:command isCopy:YES];
+}
+
+- (void)moveTo:(CDVInvokedUrlCommand*)command
+{
+    [self doCopyMove:command isCopy:NO];
 }
 
 /* Copy/move a file or directory to a new location
@@ -901,329 +1375,57 @@ NSString* const kCDVFilesystemURLPrefix = @"filesystem";
     NSString* srcURLstr = [arguments objectAtIndex:0];
     NSString* destURLstr = [arguments objectAtIndex:1];
 
-    __block CDVPluginResult* result = nil;
-    CDVFileError errCode = 0;  // !! Currently 0 is not defined, use this to signal error !!
+    CDVPluginResult *result;
 
     if (!srcURLstr || !destURLstr) {
         // either no source or no destination provided
-        errCode = NOT_FOUND_ERR;
-    } else {
-    CDVFilesystemURL* srcURL = [self fileSystemURLWithString:srcURLstr];
-    CDVFilesystemURL* destURL = [self fileSystemURLWithString:destURLstr];
-    NSString* srcFullPath = srcURL.fileSystemPath;
-    NSString* destRootPath = destURL.fileSystemPath;
-    
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVFilesystemURL* srcURL = [CDVFilesystemURL fileSystemURLWithString:srcURLstr];
+    CDVFilesystemURL* destURL = [CDVFilesystemURL fileSystemURLWithString:destURLstr];
+
+    CDVLocalFilesystem *srcFs = [self.fileSystems objectAtIndex:srcURL.fileSystemType];
+    CDVLocalFilesystem *destFs = [self.fileSystems objectAtIndex:destURL.fileSystemType];
+
     // optional argument; use last component from srcFullPath if new name not provided
-    NSString* newName = ([arguments count] > 2) ? [arguments objectAtIndex:2] : [srcFullPath lastPathComponent];
+    NSString* newName = ([arguments count] > 2) ? [arguments objectAtIndex:2] : [srcURL.url lastPathComponent];
     if ([newName rangeOfString:@":"].location != NSNotFound) {
         // invalid chars in new name
-        errCode = ENCODING_ERR;
-    } else {
-        NSString* newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName];
-        NSString* newFullPath = [self fullPathForFileSystemPath:newFileSystemPath];
-        
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        if ([newFileSystemPath isEqualToString:srcFullPath]) {
-            // source and destination can not be the same
-            errCode = INVALID_MODIFICATION_ERR;
-        } else if ([[srcURL.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-            if (bCopy) {
-                // Copying (as opposed to moving) an assets library file is okay.
-                // In this case, we need to use an asynchronous method to retrieve the file.
-                // Because of this, we can't just assign to `result` and send it at the end of the method.
-                // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
-                ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
-                    if (asset) {
-                        // We have the asset!  Get the data and try to copy it over.
-                        if (![fileMgr fileExistsAtPath:destRootPath]) {
-                            // The destination directory doesn't exist.
-                            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-                            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-                            return;
-                        } else if ([fileMgr fileExistsAtPath:newFileSystemPath]) {
-                            // A file already exists at the destination path.
-                            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:PATH_EXISTS_ERR];
-                            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-                            return;
-                        }
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
 
-                        // We're good to go!  Write the file to the new destination.
-                        ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
-                        Byte* buffer = (Byte*)malloc([assetRepresentation size]);
-                        NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
-                        NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
-                        [data writeToFile:newFileSystemPath atomically:YES];
-                        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:newFileSystemPath isDirectory:NO]];
-                        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-                    } else {
-                        // We couldn't find the asset.  Send the appropriate error.
-                        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-                        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-                    }
-                };
-                ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
-                    // Retrieving the asset failed for some reason.  Send the appropriate error.
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
-                    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-                };
-
-                ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
-                [assetsLibrary assetForURL:[NSURL URLWithString:srcFullPath] resultBlock:resultBlock failureBlock:failureBlock];
-                return;
-            } else {
-                // Moving an assets library file is not doable, since we can't remove it.
-                errCode = INVALID_MODIFICATION_ERR;
-            }
-        } else {
-            BOOL bSrcIsDir = NO;
-            BOOL bDestIsDir = NO;
-            BOOL bNewIsDir = NO;
-            BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
-            BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
-            BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir];
-            if (!bSrcExists || !bDestExists) {
-                // the source or the destination root does not exist
-                errCode = NOT_FOUND_ERR;
-            } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) {
-                // can't copy/move dir to file
-                errCode = INVALID_MODIFICATION_ERR;
-            } else { // no errors yet
-                NSError* __autoreleasing error = nil;
-                BOOL bSuccess = NO;
-                if (bCopy) {
-                    if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
-                        // can't copy dir into self
-                        errCode = INVALID_MODIFICATION_ERR;
-                    } else if (bNewExists) {
-                        // the full destination should NOT already exist if a copy
-                        errCode = PATH_EXISTS_ERR;
-                    } else {
-                        bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
-                    }
-                } else { // move
-                    // iOS requires that destination must not exist before calling moveTo
-                    // is W3C INVALID_MODIFICATION_ERR error if destination dir exists and has contents
-                    //
-                    if (!bSrcIsDir && (bNewExists && bNewIsDir)) {
-                        // can't move a file to directory
-                        errCode = INVALID_MODIFICATION_ERR;
-                    } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
-                        // can't move a dir into itself
-                        errCode = INVALID_MODIFICATION_ERR;
-                    } else if (bNewExists) {
-                        if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) {
-                            // can't move dir to a dir that is not empty
-                            errCode = INVALID_MODIFICATION_ERR;
-                            newFileSystemPath = nil;  // so we won't try to move
-                        } else {
-                            // remove destination so can perform the moveItemAtPath
-                            bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL];
-                            if (!bSuccess) {
-                                errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
-                                newFileSystemPath = nil;
-                            }
-                        }
-                    } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) {
-                        // can't move a directory inside itself or to any child at any depth;
-                        errCode = INVALID_MODIFICATION_ERR;
-                        newFileSystemPath = nil;
-                    }
+    [destFs copyFileToURL:destURL withName:newName fromFileSystem:srcFs atURL:srcURL copy:bCopy callback:^(CDVPluginResult* result) {
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    }];
 
-                    if (newFileSystemPath != nil) {
-                        bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
-                    }
-                }
-                if (bSuccess) {
-                    // should verify it is there and of the correct type???
-                    NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:srcURL.fileSystemType isDirectory:bSrcIsDir];  // should be the same type as source
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
-                } else {
-                    errCode = INVALID_MODIFICATION_ERR; // catch all
-                    if (error) {
-                        if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) {
-                            errCode = NOT_READABLE_ERR;
-                        } else if ([error code] == NSFileWriteOutOfSpaceError) {
-                            errCode = QUOTA_EXCEEDED_ERR;
-                        } else if ([error code] == NSFileWriteNoPermissionError) {
-                            errCode = NO_MODIFICATION_ALLOWED_ERR;
-                        }
-                    }
-                }
-            }
-        }
-    }
-    }
-    if (errCode > 0) {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
-    }
-    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
 - (void)getFileMetadata:(CDVInvokedUrlCommand*)command
 {
     // arguments
     NSString* localURIstr = [command.arguments objectAtIndex:0];
-    CDVFilesystemURL* localURI = [self fileSystemURLWithString:localURIstr];
-    
-    __block CDVPluginResult* result = nil;
+    CDVFilesystemURL* localURI = [CDVFilesystemURL fileSystemURLWithString:localURIstr];
+    CDVLocalFilesystem *fs = [self.fileSystems objectAtIndex:localURI.fileSystemType];
 
-    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
-        // In this case, we need to use an asynchronous method to retrieve the file.
-        // Because of this, we can't just assign to `result` and send it at the end of the method.
-        // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
-        ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
-            if (asset) {
-                // We have the asset!  Populate the dictionary and send it off.
-                NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
-                ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
-                [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
-                [fileInfo setObject:localURIstr forKey:@"fullPath"];
-                NSString* filename = [assetRepresentation filename];
-                [fileInfo setObject:filename forKey:@"name"];
-                [fileInfo setObject:[self getMimeTypeFromPath:filename] forKey:@"type"];
-                NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
-                NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
-                [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
-                
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
-                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-            } else {
-                // We couldn't find the asset.  Send the appropriate error.
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-            }
-        };
-        ALAssetsLibraryAccessFailu

<TRUNCATED>

[5/6] CB-5406: Split iOS file plugin into modules

Posted by ia...@apache.org.
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/7bb39777/src/ios/CDVLocalFilesystem.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVLocalFilesystem.m b/src/ios/CDVLocalFilesystem.m
new file mode 100644
index 0000000..0864017
--- /dev/null
+++ b/src/ios/CDVLocalFilesystem.m
@@ -0,0 +1,655 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+ */
+
+#import "CDVFile.h"
+#import "CDVLocalFilesystem.h"
+#import <Cordova/CDV.h>
+#import <MobileCoreServices/MobileCoreServices.h>
+#import <sys/xattr.h>
+
+@implementation CDVLocalFilesystem
+@synthesize fsRoot=_fsRoot;
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot
+{
+    if (self) {
+        _fsRoot = fsRoot;
+    }
+    return self;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the
+ *   URI represents a non-existent path, or is unrecognized or otherwise malformed.
+ */
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
+{
+    CDVPluginResult* result = nil;
+    NSString *path = [self fileSystemPathForURL:url];
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    BOOL isDir = NO;
+    // see if exists and is file or dir
+    BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir];
+    if (bExists) {
+        NSDictionary* fileSystem = [self makeEntryForPath:url.fullPath fileSystem:url.fileSystemType isDirectory:isDir];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+    } else {
+        // return NOT_FOUND_ERR
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    return result;
+}
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir
+{
+    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+    NSString* lastPart = [fullPath lastPathComponent];
+    if (isDir && ![fullPath hasSuffix:@"/"]) {
+        fullPath = [fullPath stringByAppendingString:@"/"];
+    }
+    [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
+    [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
+    [dirEntry setObject:fullPath forKey:@"fullPath"];
+    [dirEntry setObject:lastPart forKey:@"name"];
+    [dirEntry setObject: [NSNumber numberWithInt:fsType] forKey: @"filesystem"];
+
+    return dirEntry;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ *  or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fileSystemPathForURL:(CDVFilesystemURL *)url
+{
+    NSString *path = nil;
+    NSString *fullPath = url.fullPath;
+    path = [NSString stringWithFormat:@"%@%@", self.fsRoot, fullPath];
+    if ([path hasSuffix:@"/"]) {
+      path = [path substringToIndex:([path length]-1)];
+    }
+    return path;
+}
+
+
+- (CDVPluginResult *)getFileForURL:(CDVFilesystemURL *)baseURI requestedPath:(NSString *)requestedPath options:(NSDictionary *)options
+{
+    CDVPluginResult* result = nil;
+    BOOL bDirRequest = NO;
+    BOOL create = NO;
+    BOOL exclusive = NO;
+    int errorCode = 0;  // !!! risky - no error code currently defined for 0
+
+    if ([options valueForKeyIsNumber:@"create"]) {
+        create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
+    }
+    if ([options valueForKeyIsNumber:@"exclusive"]) {
+        exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
+    }
+
+    if ([options valueForKeyIsNumber:@"getDir"]) {
+        // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method
+        bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue];
+    }
+    // see if the requested path has invalid characters - should we be checking for  more than just ":"?
+    if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
+        errorCode = ENCODING_ERR;
+    } else {
+        CDVFilesystemURL* requestedURL = [CDVFilesystemURL fileSystemURLWithURL:[NSURL URLWithString:[requestedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] relativeToURL:baseURI.url]]; /* TODO: UGLY - FIX */
+
+        // NSLog(@"reqFullPath = %@", reqFullPath);
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir;
+        BOOL bExists = [fileMgr fileExistsAtPath:[self fileSystemPathForURL:requestedURL] isDirectory:&bIsDir];
+        if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
+            // path exists and is of requested type  - return TYPE_MISMATCH_ERR
+            errorCode = TYPE_MISMATCH_ERR;
+        } else if (!bExists && (create == NO)) {
+            // path does not exist and create is false - return NOT_FOUND_ERR
+            errorCode = NOT_FOUND_ERR;
+        } else if (bExists && (create == YES) && (exclusive == YES)) {
+            // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR
+            errorCode = PATH_EXISTS_ERR;
+        } else {
+            // if bExists and create == YES - just return data
+            // if bExists and create == NO  - just return data
+            // if !bExists and create == YES - create and return data
+            BOOL bSuccess = YES;
+            NSError __autoreleasing* pError = nil;
+            if (!bExists && (create == YES)) {
+                if (bDirRequest) {
+                    // create the dir
+                    bSuccess = [fileMgr createDirectoryAtPath:[self fileSystemPathForURL:requestedURL] withIntermediateDirectories:NO attributes:nil error:&pError];
+                } else {
+                    // create the empty file
+                    bSuccess = [fileMgr createFileAtPath:[self fileSystemPathForURL:requestedURL] contents:nil attributes:nil];
+                }
+            }
+            if (!bSuccess) {
+                errorCode = ABORT_ERR;
+                if (pError) {
+                    NSLog(@"error creating directory: %@", [pError localizedDescription]);
+                }
+            } else {
+                // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]);
+                // file existed or was created
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath fileSystem:baseURI.fileSystemType isDirectory:bDirRequest]];
+            }
+        } // are all possible conditions met?
+    }
+
+    if (errorCode > 0) {
+        // create error callback
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+    }
+    return result;
+
+}
+
+- (CDVPluginResult*)getParentForURL:(CDVFilesystemURL *)localURI
+{
+    CDVPluginResult* result = nil;
+    CDVFilesystemURL *newURI = nil;
+    if ([localURI.fullPath isEqualToString:@""]) {
+        // return self
+        newURI = localURI;
+    } else {
+        newURI = [CDVFilesystemURL fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */
+    }
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    BOOL bIsDir;
+    BOOL bExists = [fileMgr fileExistsAtPath:[self fileSystemPathForURL:newURI] isDirectory:&bIsDir];
+    if (bExists) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath fileSystem:newURI.fileSystemType isDirectory:bIsDir]];
+    } else {
+        // invalid path or file does not exist
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    return result;
+}
+
+- (void)getMetadataForURL:(CDVFilesystemURL *)url callback:(void (^)(CDVPluginResult *))callback
+{
+
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    NSError* __autoreleasing error = nil;
+
+    CDVPluginResult *result;
+    NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:[self  fileSystemPathForURL:url] error:&error];
+
+    if (fileAttribs) {
+        NSDate* modDate = [fileAttribs fileModificationDate];
+        if (modDate) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[modDate timeIntervalSince1970] * 1000];
+        }
+    } else {
+        // didn't get fileAttribs
+        CDVFileError errorCode = ABORT_ERR;
+        NSLog(@"error getting metadata: %@", [error localizedDescription]);
+        if ([error code] == NSFileNoSuchFileError) {
+            errorCode = NOT_FOUND_ERR;
+        }
+        // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
+    }
+    if (!result) {
+        // invalid path or file does not exist
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION];
+    }
+    callback(result);
+}
+
+- (CDVPluginResult*)setMetadataForURL:(CDVFilesystemURL *)localURI withObject:(NSDictionary *)options
+{
+    BOOL ok = NO;
+
+    NSString* filePath = [self fileSystemPathForURL:localURI];
+    // we only care about this iCloud key for now.
+    // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
+    NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
+    id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
+
+    if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
+        if (IsAtLeastiOSVersion(@"5.1")) {
+            NSURL* url = [NSURL fileURLWithPath:filePath];
+            NSError* __autoreleasing error = nil;
+
+            ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
+        } else { // below 5.1 (deprecated - only really supported in 5.01)
+            u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
+            if (value == 0) { // remove the attribute (allow backup, the default)
+                ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
+            } else { // set the attribute (skip backup)
+                ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
+            }
+        }
+    }
+
+    if (ok) {
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+    } else {
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+    }
+}
+
+/* remove the file or directory (recursively)
+ * IN:
+ * NSString* fullPath - the full path to the file or directory to be removed
+ * NSString* callbackId
+ * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling
+ */
+
+- (CDVPluginResult*)doRemove:(NSString*)fullPath
+{
+    CDVPluginResult* result = nil;
+    BOOL bSuccess = NO;
+    NSError* __autoreleasing pError = nil;
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+    @try {
+        bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError];
+        if (bSuccess) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+        } else {
+            // see if we can give a useful error
+            CDVFileError errorCode = ABORT_ERR;
+            NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]);
+            if ([pError code] == NSFileNoSuchFileError) {
+                errorCode = NOT_FOUND_ERR;
+            } else if ([pError code] == NSFileWriteNoPermissionError) {
+                errorCode = NO_MODIFICATION_ALLOWED_ERR;
+            }
+
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+        }
+    } @catch(NSException* e) {  // NSInvalidArgumentException if path is . or ..
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR];
+    }
+
+    return result;
+}
+
+- (CDVPluginResult *)removeFileAtURL:(CDVFilesystemURL *)localURI
+{
+    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
+
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    BOOL bIsDir = NO;
+    BOOL bExists = [fileMgr fileExistsAtPath:fileSystemPath isDirectory:&bIsDir];
+    if (!bExists) {
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fileSystemPath error:nil] count] != 0)) {
+        // dir is not empty
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:INVALID_MODIFICATION_ERR];
+    }
+    return [self doRemove:fileSystemPath];
+}
+
+- (CDVPluginResult *)recursiveRemoveFileAtURL:(CDVFilesystemURL *)localURI
+{
+    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
+    return [self doRemove:fileSystemPath];
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ *  or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath
+{
+    if ([fsPath hasPrefix:self.fsRoot]) {
+        return [fsPath substringFromIndex:[self.fsRoot length]];
+    }
+    return nil;
+}
+
+
+- (CDVPluginResult *)readEntriesAtURL:(CDVFilesystemURL *)localURI
+{
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    NSError* __autoreleasing error = nil;
+    NSString *fileSystemPath = [self fileSystemPathForURL:localURI];
+
+    NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fileSystemPath error:&error];
+
+    if (contents) {
+        NSMutableArray* entries = [NSMutableArray arrayWithCapacity:1];
+        if ([contents count] > 0) {
+            // create an Entry (as JSON) for each file/dir
+            for (NSString* name in contents) {
+                // see if is dir or file
+                NSString* entryPath = [fileSystemPath stringByAppendingPathComponent:name];
+                BOOL bIsDir = NO;
+                [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir];
+                NSDictionary* entryDict = [self makeEntryForPath:[self fullPathForFileSystemPath:entryPath] fileSystem:localURI.fileSystemType isDirectory:bIsDir];
+                [entries addObject:entryDict];
+            }
+        }
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:entries];
+    } else {
+        // assume not found but could check error for more specific error conditions
+        return [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+}
+
+- (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos
+{
+    unsigned long long newPos = 0UL;
+
+    NSFileHandle* file = [NSFileHandle fileHandleForWritingAtPath:filePath];
+
+    if (file) {
+        [file truncateFileAtOffset:(unsigned long long)pos];
+        newPos = [file offsetInFile];
+        [file synchronizeFile];
+        [file closeFile];
+    }
+    return newPos;
+}
+
+- (CDVPluginResult *)truncateFileAtURL:(CDVFilesystemURL *)localURI atPosition:(unsigned long long)pos
+{
+    unsigned long long newPos = [self truncateFile:[self fileSystemPathForURL:localURI] atPosition:pos];
+    return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:newPos];
+}
+
+- (CDVPluginResult *)writeToFileAtURL:(CDVFilesystemURL *)localURL withData:(NSData*)encData append:(BOOL)shouldAppend
+{
+    NSString *filePath = [self fileSystemPathForURL:localURL];
+
+    CDVPluginResult* result = nil;
+    CDVFileError errCode = INVALID_MODIFICATION_ERR;
+    int bytesWritten = 0;
+
+    if (filePath) {
+        NSOutputStream* fileStream = [NSOutputStream outputStreamToFileAtPath:filePath append:shouldAppend];
+        if (fileStream) {
+            NSUInteger len = [encData length];
+            [fileStream open];
+
+            bytesWritten = [fileStream write:[encData bytes] maxLength:len];
+
+            [fileStream close];
+            if (bytesWritten > 0) {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:bytesWritten];
+                // } else {
+                // can probably get more detailed error info via [fileStream streamError]
+                // errCode already set to INVALID_MODIFICATION_ERR;
+                // bytesWritten = 0; // may be set to -1 on error
+            }
+        } // else fileStream not created return INVALID_MODIFICATION_ERR
+    } else {
+        // invalid filePath
+        errCode = NOT_FOUND_ERR;
+    }
+    if (!result) {
+        // was an error
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+    }
+    return result;
+}
+
+/**
+ * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name,
+ * or attempted to copy a directory into a directory that it contains directly or indirectly.
+ *
+ * IN:
+ *  NSString* srcDir
+ *  NSString* destinationDir
+ * OUT:
+ *  YES copy/ move is allows
+ *  NO move is onto itself
+ */
+- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest
+{
+    // This weird test is to determine if we are copying or moving a directory into itself.
+    // Copy /Documents/myDir to /Documents/myDir-backup is okay but
+    // Copy /Documents/myDir to /Documents/myDir/backup not okay
+    BOOL copyOK = YES;
+    NSRange range = [dest rangeOfString:src];
+
+    if (range.location != NSNotFound) {
+        NSRange testRange = {range.length - 1, ([dest length] - range.length)};
+        NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange];
+        if (resultRange.location != NSNotFound) {
+            copyOK = NO;
+        }
+    }
+    return copyOK;
+}
+
+- (void)copyFileToURL:(CDVFilesystemURL *)destURL withName:(NSString *)newName fromFileSystem:(NSObject<CDVFileSystem> *)srcFs atURL:(CDVFilesystemURL *)srcURL copy:(BOOL)bCopy callback:(void (^)(CDVPluginResult *))callback
+{
+    NSFileManager *fileMgr = [[NSFileManager alloc] init];
+    NSString *destRootPath = [self fileSystemPathForURL:destURL];
+    BOOL bDestIsDir = NO;
+    BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
+
+    NSString *newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName];
+    NSString *newFullPath = [self fullPathForFileSystemPath:newFileSystemPath];
+
+    BOOL bNewIsDir = NO;
+    BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir];
+
+    CDVPluginResult *result = nil;
+    int errCode = 0;
+
+    if (!bDestExists) {
+        // the destination root does not exist
+        errCode = NOT_FOUND_ERR;
+    }
+
+    else if ([srcFs isKindOfClass:[CDVLocalFilesystem class]]) {
+        /* Same FS, we can shortcut with NSFileManager operations */
+        NSString *srcFullPath = [self fileSystemPathForURL:srcURL];
+
+        BOOL bSrcIsDir = NO;
+        BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
+
+        if (!bSrcExists) {
+            // the source does not exist
+            errCode = NOT_FOUND_ERR;
+        } else if ([newFileSystemPath isEqualToString:srcFullPath]) {
+            // source and destination can not be the same
+            errCode = INVALID_MODIFICATION_ERR;
+        } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) {
+            // can't copy/move dir to file
+            errCode = INVALID_MODIFICATION_ERR;
+        } else { // no errors yet
+            NSError* __autoreleasing error = nil;
+            BOOL bSuccess = NO;
+            if (bCopy) {
+                if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+                    // can't copy dir into self
+                    errCode = INVALID_MODIFICATION_ERR;
+                } else if (bNewExists) {
+                    // the full destination should NOT already exist if a copy
+                    errCode = PATH_EXISTS_ERR;
+                } else {
+                    bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+                }
+            } else { // move
+                // iOS requires that destination must not exist before calling moveTo
+                // is W3C INVALID_MODIFICATION_ERR error if destination dir exists and has contents
+                //
+                if (!bSrcIsDir && (bNewExists && bNewIsDir)) {
+                    // can't move a file to directory
+                    errCode = INVALID_MODIFICATION_ERR;
+                } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
+                    // can't move a dir into itself
+                    errCode = INVALID_MODIFICATION_ERR;
+                } else if (bNewExists) {
+                    if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) {
+                        // can't move dir to a dir that is not empty
+                        errCode = INVALID_MODIFICATION_ERR;
+                        newFileSystemPath = nil;  // so we won't try to move
+                    } else {
+                        // remove destination so can perform the moveItemAtPath
+                        bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL];
+                        if (!bSuccess) {
+                            errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
+                            newFileSystemPath = nil;
+                        }
+                    }
+                } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) {
+                    // can't move a directory inside itself or to any child at any depth;
+                    errCode = INVALID_MODIFICATION_ERR;
+                    newFileSystemPath = nil;
+                }
+
+                if (newFileSystemPath != nil) {
+                    bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
+                }
+            }
+            if (bSuccess) {
+                // should verify it is there and of the correct type???
+                NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:srcURL.fileSystemType isDirectory:bSrcIsDir];  // should be the same type as source
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+            } else {
+                if (error) {
+                    if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) {
+                        errCode = NOT_READABLE_ERR;
+                    } else if ([error code] == NSFileWriteOutOfSpaceError) {
+                        errCode = QUOTA_EXCEEDED_ERR;
+                    } else if ([error code] == NSFileWriteNoPermissionError) {
+                        errCode = NO_MODIFICATION_ALLOWED_ERR;
+                    }
+                }
+            }
+        }
+    } else {
+        // Need to copy the hard way
+        [srcFs readFileAtURL:srcURL start:0 end:-1 callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+            CDVPluginResult* result = nil;
+            if (data != nil) {
+                BOOL bSuccess = [data writeToFile:newFileSystemPath atomically:YES];
+                if (bSuccess) {
+                    // should verify it is there and of the correct type???
+                    NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:destURL.fileSystemType isDirectory:NO];
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+                } else {
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ABORT_ERR];
+                }
+            } else {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+            }
+            callback(result);
+        }];
+        return; // Async IO; return without callback.
+    }
+    if (result == nil) {
+        if (!errCode) {
+            errCode = INVALID_MODIFICATION_ERR; // Catch-all default
+        }
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
+    }
+    callback(result);
+}
+
+/* helper function to get the mimeType from the file extension
+ * IN:
+ *	NSString* fullPath - filename (may include path)
+ * OUT:
+ *	NSString* the mime type as type/subtype.  nil if not able to determine
+ */
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+    NSString* mimeType = nil;
+
+    if (fullPath) {
+        CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
+        if (typeId) {
+            mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
+            if (!mimeType) {
+                // special case for m4a
+                if ([(__bridge NSString*)typeId rangeOfString : @"m4a-audio"].location != NSNotFound) {
+                    mimeType = @"audio/mp4";
+                } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
+                    mimeType = @"audio/wav";
+                }
+            }
+            CFRelease(typeId);
+        }
+    }
+    return mimeType;
+}
+
+- (void)readFileAtURL:(CDVFilesystemURL *)localURL start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString* mimeType, CDVFileError))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+
+    NSString* mimeType = [CDVLocalFilesystem getMimeTypeFromPath:path];
+    if (mimeType == nil) {
+        mimeType = @"*/*";
+    }
+    NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:path];
+    if (start > 0) {
+        [file seekToFileOffset:start];
+    }
+
+    NSData* readData;
+    if (end < 0) {
+        readData = [file readDataToEndOfFile];
+    } else {
+        readData = [file readDataOfLength:(end - start)];
+    }
+    [file closeFile];
+
+    callback(readData, mimeType, readData != nil ? NO_ERROR : NOT_FOUND_ERR);
+}
+
+- (void)getFileMetadataForURL:(CDVFilesystemURL *)localURL callback:(void (^)(CDVPluginResult *))callback
+{
+    NSString *path = [self fileSystemPathForURL:localURL];
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir = NO;
+        // make sure it exists and is not a directory
+        BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&bIsDir];
+        if (!bExists || bIsDir) {
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR]);
+        } else {
+            // create dictionary of file info
+            NSError* __autoreleasing error = nil;
+            NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:path error:&error];
+            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
+            [fileInfo setObject:localURL.fullPath forKey:@"fullPath"];
+            [fileInfo setObject:@"" forKey:@"type"];  // can't easily get the mimetype unless create URL, send request and read response so skipping
+            [fileInfo setObject:[path lastPathComponent] forKey:@"name"];
+            NSDate* modDate = [fileAttrs fileModificationDate];
+            NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
+            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+            callback([CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo]);
+        }
+}
+
+@end


[4/6] git commit: CB-5406: Factor out filesystem providers in iOS

Posted by ia...@apache.org.
CB-5406: Factor out filesystem providers in iOS


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/64187ca1
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/64187ca1
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/64187ca1

Branch: refs/heads/dev
Commit: 64187ca1be3e5b0a8d366959d1946a99ba44c198
Parents: b8c4f85
Author: Ian Clelland <ic...@chromium.org>
Authored: Mon Nov 18 14:49:56 2013 -0500
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Nov 18 14:54:01 2013 -0500

----------------------------------------------------------------------
 src/ios/CDVFile.h |   19 +-
 src/ios/CDVFile.m | 2059 +++++++++++++++++++++++++-----------------------
 2 files changed, 1099 insertions(+), 979 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/64187ca1/src/ios/CDVFile.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVFile.h b/src/ios/CDVFile.h
index c5877f1..ef17a62 100644
--- a/src/ios/CDVFile.h
+++ b/src/ios/CDVFile.h
@@ -39,7 +39,8 @@ typedef int CDVFileError;
 
 enum CDVFileSystemType {
     TEMPORARY = 0,
-    PERSISTENT = 1
+    PERSISTENT = 1,
+    ASSETS_LIBRARY = 2,
 };
 typedef int CDVFileSystemType;
 
@@ -52,13 +53,12 @@ extern NSString* const kCDVAssetsLibraryPrefix;
     NSString* persistentPath;
     NSString* temporaryPath;
 
+    NSMutableArray* fileSystems_;
     BOOL userHasAllowed;
 }
+
 - (NSNumber*)checkFreeDiskSpace:(NSString*)appPath;
-- (NSString*)getAppPath:(NSString*)pathFragment;
-- (NSDictionary*)getDirectoryEntry:(NSString*)fullPath isDirectory:(BOOL)isDir; //DEPRECATE HARD
 - (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir;
-- (NSString *)fileSystemPathForLocalURI:(NSURL *)uri;
 
 /* Exec API */
 - (void)requestFileSystem:(CDVInvokedUrlCommand*)command;
@@ -81,22 +81,15 @@ extern NSString* const kCDVAssetsLibraryPrefix;
 - (void)testDirectoryExists:(CDVInvokedUrlCommand*)command;
 - (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command;
 - (void)truncate:(CDVInvokedUrlCommand*)command;
-
-- (CDVPluginResult*)doRemove:(NSString*)fullPath;
-- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest;
 - (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy;
-- (void)readFileWithPath:(NSString*)path start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString*, CDVFileError))callback;
-- (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
-
-- (void)writeToFile:(NSString*)fileName withData:(NSData*)data append:(BOOL)shouldAppend callback:(NSString*)callbackId;
-- (void)writeToFile:(NSString*)fileName withString:(NSString*)data encoding:(NSStringEncoding)encoding append:(BOOL)shouldAppend callback:(NSString*)callbackId;
-- (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos;
 
 @property (nonatomic, strong) NSString* appDocsPath;
 @property (nonatomic, strong) NSString* appLibraryPath;
 @property (nonatomic, strong) NSString* appTempPath;
 @property (nonatomic, strong) NSString* persistentPath;
 @property (nonatomic, strong) NSString* temporaryPath;
+@property (nonatomic, strong) NSMutableArray* fileSystems;
+
 @property BOOL userHasAllowed;
 
 @end


[2/6] git commit: CB-5406: Update iOS native code to use filesystem URLs internally

Posted by ia...@apache.org.
CB-5406: Update iOS native code to use filesystem URLs internally


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/b3b6a83f
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/b3b6a83f
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/b3b6a83f

Branch: refs/heads/dev
Commit: b3b6a83f0c18d0416245b4bc7375ac07fa819129
Parents: 1dfdf0f
Author: Ian Clelland <ic...@chromium.org>
Authored: Mon Nov 18 13:26:54 2013 -0500
Committer: Ian Clelland <ic...@chromium.org>
Committed: Mon Nov 18 14:53:40 2013 -0500

----------------------------------------------------------------------
 plugin.xml            |   5 +-
 src/ios/CDVFile.h     |  24 +-
 src/ios/CDVFile.m     | 688 ++++++++++++++++++++++++---------------------
 www/ios/FileSystem.js |  27 ++
 4 files changed, 417 insertions(+), 327 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/b3b6a83f/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 0cc310d..d4091f8 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -115,9 +115,12 @@ xmlns:android="http://schemas.android.com/apk/res/android"
         <source-file src="src/ios/CDVFile.m" />
 
         <!-- ios specific file apis -->
-        <js-module src="www/ios/Entry.js" name="Entry1">
+        <js-module src="www/ios/Entry.js" name="iosEntry">
             <merges target="window.Entry" />
         </js-module>
+        <js-module src="www/ios/FileSystem.js" name="iosFileSystem">
+            <merges target="window.FileSystem" />
+        </js-module>
 
         <framework src="AssetsLibrary.framework" />
         <framework src="MobileCoreServices.framework" />

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/b3b6a83f/src/ios/CDVFile.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVFile.h b/src/ios/CDVFile.h
index 54d25f0..c5877f1 100644
--- a/src/ios/CDVFile.h
+++ b/src/ios/CDVFile.h
@@ -56,9 +56,12 @@ extern NSString* const kCDVAssetsLibraryPrefix;
 }
 - (NSNumber*)checkFreeDiskSpace:(NSString*)appPath;
 - (NSString*)getAppPath:(NSString*)pathFragment;
-// -(NSString*) getFullPath: (NSString*)pathFragment;
+- (NSDictionary*)getDirectoryEntry:(NSString*)fullPath isDirectory:(BOOL)isDir; //DEPRECATE HARD
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir;
+- (NSString *)fileSystemPathForLocalURI:(NSURL *)uri;
+
+/* Exec API */
 - (void)requestFileSystem:(CDVInvokedUrlCommand*)command;
-- (NSDictionary*)getDirectoryEntry:(NSString*)fullPath isDirectory:(BOOL)isDir;
 - (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command;
 - (void)getDirectory:(CDVInvokedUrlCommand*)command;
 - (void)getFile:(CDVInvokedUrlCommand*)command;
@@ -66,30 +69,25 @@ extern NSString* const kCDVAssetsLibraryPrefix;
 - (void)getMetadata:(CDVInvokedUrlCommand*)command;
 - (void)removeRecursively:(CDVInvokedUrlCommand*)command;
 - (void)remove:(CDVInvokedUrlCommand*)command;
-- (CDVPluginResult*)doRemove:(NSString*)fullPath;
 - (void)copyTo:(CDVInvokedUrlCommand*)command;
 - (void)moveTo:(CDVInvokedUrlCommand*)command;
-- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest;
-- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy;
-// - (void) toURI:(CDVInvokedUrlCommand*)command;
 - (void)getFileMetadata:(CDVInvokedUrlCommand*)command;
 - (void)readEntries:(CDVInvokedUrlCommand*)command;
-
 - (void)readAsText:(CDVInvokedUrlCommand*)command;
 - (void)readAsDataURL:(CDVInvokedUrlCommand*)command;
 - (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command;
-- (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
 - (void)write:(CDVInvokedUrlCommand*)command;
 - (void)testFileExists:(CDVInvokedUrlCommand*)command;
 - (void)testDirectoryExists:(CDVInvokedUrlCommand*)command;
-// - (void) createDirectory:(CDVInvokedUrlCommand*)command;
-// - (void) deleteDirectory:(CDVInvokedUrlCommand*)command;
-// - (void) deleteFile:(CDVInvokedUrlCommand*)command;
 - (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command;
 - (void)truncate:(CDVInvokedUrlCommand*)command;
 
-// - (BOOL) fileExists:(NSString*)fileName;
-// - (BOOL) directoryExists:(NSString*)dirName;
+- (CDVPluginResult*)doRemove:(NSString*)fullPath;
+- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest;
+- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy;
+- (void)readFileWithPath:(NSString*)path start:(NSInteger)start end:(NSInteger)end callback:(void (^)(NSData*, NSString*, CDVFileError))callback;
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
+
 - (void)writeToFile:(NSString*)fileName withData:(NSData*)data append:(BOOL)shouldAppend callback:(NSString*)callbackId;
 - (void)writeToFile:(NSString*)fileName withString:(NSString*)data encoding:(NSStringEncoding)encoding append:(BOOL)shouldAppend callback:(NSString*)callbackId;
 - (unsigned long long)truncateFile:(NSString*)filePath atPosition:(unsigned long long)pos;

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/b3b6a83f/src/ios/CDVFile.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVFile.m b/src/ios/CDVFile.m
index 6b2602d..2f41046 100644
--- a/src/ios/CDVFile.m
+++ b/src/ios/CDVFile.m
@@ -25,6 +25,43 @@
 #import <MobileCoreServices/MobileCoreServices.h>
 #import <sys/xattr.h>
 
+@interface CDVFilesystemURL : NSObject  {
+    NSURL *url_;
+    CDVFileSystemType fileSystemType_;
+    NSString *fullPath_;
+    NSString *fileSystemPath_;
+}
+
+- (id) initWithString:(NSString*)strURL;
+- (id) initWithURL:(NSURL*)URL;
+
+@property (atomic) NSURL *url;
+@property (atomic) CDVFileSystemType fileSystemType;
+@property (atomic) NSString *fullPath;
+@property (atomic) NSString *fileSystemPath;
+
+@end
+
+@implementation CDVFilesystemURL
+@synthesize url=url_;
+@synthesize fileSystemType=fileSystemType_;
+@synthesize fullPath=fullPath_, fileSystemPath=fileSystemPath_;
+
+- (id) initWithString:(NSString *)strURL
+{
+  NSURL *decodedURL = [NSURL URLWithString:strURL];
+  return [self initWithURL:decodedURL];
+}
+
+-(id) initWithURL:(NSURL *)URL
+{
+  url_ = URL;
+  fileSystemType_ = -42;
+  return self;
+}
+
+@end
+
 extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
 
 #ifndef __IPHONE_5_1
@@ -32,6 +69,7 @@ extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import)
 #endif
 
 NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
+NSString* const kCDVAssetsLibraryScheme = @"assets-library";
 
 @implementation CDVFile
 
@@ -58,6 +96,116 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
     return self;
 }
 
+- (void)updateFileSystemURL:(CDVFilesystemURL *)fsURL;
+{
+    fsURL.fileSystemType = [self filesystemTypeForLocalURI:fsURL.url];
+    fsURL.fileSystemPath = [self fileSystemPathForLocalURI:fsURL.url];
+    fsURL.fullPath = [self fullPathForFileSystemPath:fsURL.fileSystemPath];
+}
+    
+- (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL
+{
+    CDVFilesystemURL* fsURL = [[CDVFilesystemURL alloc] initWithString:strURL];
+    if (fsURL) {
+        [self updateFileSystemURL:fsURL];
+    }
+    return fsURL;
+}
+    
+- (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL
+{
+    CDVFilesystemURL* fsURL = [[CDVFilesystemURL alloc] initWithURL:URL];
+    if (fsURL) {
+        [self updateFileSystemURL:fsURL];
+    }
+    return fsURL;
+}
+    
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ *  or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fileSystemPathForLocalURI:(NSURL *)uri
+{
+    NSString *path = nil;
+    int fsType = [self filesystemTypeForLocalURI:uri];
+    NSString *fullPath = [self fullPathForLocalURI:uri];
+    if (fsType == TEMPORARY) {
+        path = [NSString stringWithFormat:@"%@%@", self.appTempPath, fullPath];
+    } else if (fsType == PERSISTENT) {
+        path = [NSString stringWithFormat:@"%@%@", self.appDocsPath, fullPath];
+    } else {
+        return nil;
+    }
+    if ([path hasSuffix:@"/"]) {
+      path = [path substringToIndex:([path length]-1)];
+    }
+
+    return path;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString full local filesystem path for the represented file or directory, or nil if no such path is possible
+ *  The file or directory does not necessarily have to exist. nil is returned if the filesystem type is not recognized,
+ *  or if the URL is malformed.
+ * The incoming URI should be properly escaped (no raw spaces, etc. URI percent-encoding is expected).
+ */
+- (NSString *)fullPathForFileSystemPath:(NSString *)fsPath
+{
+    if ([fsPath hasPrefix:self.appTempPath]) {
+        return [fsPath substringFromIndex:[self.appTempPath length]];
+    } else if ([fsPath hasPrefix:self.appDocsPath]) {
+        return [fsPath substringFromIndex:[self.appDocsPath length]];
+    }
+    return nil;
+}
+
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString fullPath component suitable for an Entry object.
+ * The incoming URI should be properly escaped. The returned fullPath is unescaped.
+ */
+- (NSString *)fullPathForLocalURI:(NSURL *)uri
+{
+    int fsType = [self filesystemTypeForLocalURI:uri];
+    if (fsType == TEMPORARY) {
+        return [[uri path] substringFromIndex:10];
+    } else if (fsType == PERSISTENT) {
+        return [[uri path] substringFromIndex:11];
+    }
+    return nil;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  int FileSystem type for this URI, or -1 if it is not recognized.
+ */
+- (CDVFileSystemType)filesystemTypeForLocalURI:(NSURL *)uri
+{
+    if ([[uri scheme] isEqualToString:@"filesystem"] && [[uri host] isEqualToString:@"localhost"]) {
+        if ([[uri path] hasPrefix:@"/temporary"]) {
+            return TEMPORARY;
+        } else if ([[uri path] hasPrefix:@"/persistent"]) {
+            return PERSISTENT;
+        }
+    }
+    return -1;
+}
+
+
 - (NSNumber*)checkFreeDiskSpace:(NSString*)appPath
 {
     NSFileManager* fMgr = [[NSFileManager alloc] init];
@@ -70,7 +218,8 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
     return pNumAvail;
 }
 
-// figure out if the pathFragment represents a persistent of temporary directory and return the full application path.
+// figure out if the (device local file sytstem) pathFragment represents a persistent or temporary directory
+// and return the full application path.
 // returns nil if path is not persistent or temporary
 - (NSString*)getAppPath:(NSString*)pathFragment
 {
@@ -93,31 +242,6 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
     return appPath;
 }
 
-/* get the full path to this resource
- * IN
- *	NSString* pathFragment - full Path from File or Entry object (includes system path info)
- * OUT
- *	NSString* fullPath - full iOS path to this resource,  nil if not found
- */
-
-/*  Was here in order to NOT have to return full path, but W3C synchronous DirectoryEntry.toURI() killed that idea since I can't call into iOS to
- * resolve full URI.  Leaving this code here in case W3C spec changes.
--(NSString*) getFullPath: (NSString*)pathFragment
-{
-    return pathFragment;
-    NSString* fullPath = nil;
-    NSString *appPath = [ self getAppPath: pathFragment];
-    if (appPath){
-
-        // remove last component from appPath
-        NSRange range = [appPath rangeOfString:@"/" options: NSBackwardsSearch];
-        NSString* newPath = [appPath substringToIndex:range.location];
-        // add pathFragment to get test Path
-        fullPath = [newPath stringByAppendingPathComponent:pathFragment];
-    }
-    return fullPath;
-} */
-
 /* Request the File System info
  *
  * IN:
@@ -151,8 +275,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
         NSLog(@"iOS only supports TEMPORARY and PERSISTENT file systems");
     } else {
-        // NSString* fullPath = [NSString stringWithFormat:@"/%@", (type == 0 ? [self.appTempPath lastPathComponent] : [self.appDocsPath lastPathComponent])];
-        NSString* fullPath = (type == 0 ? self.appTempPath  : self.appDocsPath);
+        NSString* fullPath = @"/";
         // check for avail space for size request
         NSNumber* pNumAvail = [self checkFreeDiskSpace:fullPath];
         // NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]);
@@ -161,7 +284,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
         } else {
             NSMutableDictionary* fileSystem = [NSMutableDictionary dictionaryWithCapacity:2];
             [fileSystem setObject:(type == TEMPORARY ? kW3FileTemporary : kW3FilePersistent) forKey:@"name"];
-            NSDictionary* dirEntry = [self getDirectoryEntry:fullPath isDirectory:YES];
+            NSDictionary* dirEntry = [self makeEntryForPath:fullPath fileSystem:type isDirectory:YES];
             [fileSystem setObject:dirEntry forKey:@"root"];
             result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
         }
@@ -169,40 +292,47 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
-/* Creates a dictionary representing an Entry Object
+/* Creates and returns a dictionary representing an Entry Object
  *
  * IN:
  * NSString* fullPath of the entry
- * FileSystem type
+ * int fsType - FileSystem type
  * BOOL isDirectory - YES if this is a directory, NO if is a file
  * OUT:
- * NSDictionary*
- Entry object
+ * NSDictionary* Entry object
  *		bool as NSNumber isDirectory
  *		bool as NSNumber isFile
  *		NSString*  name - last part of path
  *		NSString* fullPath
- *		fileSystem = FileSystem object - !! ignored because creates circular reference FileSystem contains DirectoryEntry which contains FileSystem.....!!
+ *		filesystem = FileSystem type -- actual filesystem will be created on the JS side if necessary, to avoid
+ *         creating circular reference (FileSystem contains DirectoryEntry which contains FileSystem.....!!)
  */
-- (NSDictionary*)getDirectoryEntry:(NSString*)fullPath isDirectory:(BOOL)isDir
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystem:(int)fsType isDirectory:(BOOL)isDir
 {
-    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:4];
+    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
     NSString* lastPart = [fullPath lastPathComponent];
-
+    if (isDir && ![fullPath hasSuffix:@"/"]) {
+        fullPath = [fullPath stringByAppendingString:@"/"];
+    }
     [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
     [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
-    // NSURL* fileUrl = [NSURL fileURLWithPath:fullPath];
-    // [dirEntry setObject: [fileUrl absoluteString] forKey: @"fullPath"];
     [dirEntry setObject:fullPath forKey:@"fullPath"];
     [dirEntry setObject:lastPart forKey:@"name"];
+    [dirEntry setObject: [NSNumber numberWithInt:fsType] forKey: @"filesystem"];
 
     return dirEntry;
 }
 
+/* STRONGLY DEPRECATED */
+- (NSDictionary*)getDirectoryEntry:(NSString*)fullPath isDirectory:(BOOL)isDir
+{
+  return [self makeEntryForPath:fullPath fileSystem:42 isDirectory:isDir];
+}
+
 /*
  * Given a URI determine the File System information associated with it and return an appropriate W3C entry object
  * IN
- *	NSString* fileURI  - currently requires full file URI
+ *	NSString* localURI: Should be an escaped local filesystem URI
  * OUT
  *	Entry object
  *		bool isDirectory
@@ -214,59 +344,49 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 - (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* inputUri = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* inputUri = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
+    CDVPluginResult* result = [self entryForLocalURI:inputUri];
+    if (result != nil) {
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    }
+}
 
-    // don't know if string is encoded or not so unescape
-    NSString* cleanUri = [inputUri stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
-    // now escape in order to create URL
-    NSString* strUri = [cleanUri stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
-    NSURL* testUri = [NSURL URLWithString:strUri];
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  CDVPluginResult result containing a file or directoryEntry for the localURI, or an error if the
+ *   URI represents a non-existent path, or is unrecognized or otherwise malformed.
+ */
+- (CDVPluginResult *)entryForLocalURI:(CDVFilesystemURL *)url;
+{
     CDVPluginResult* result = nil;
-
-    if (!testUri) {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
-    } else if ([testUri isFileURL]) {
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        NSString* path = [testUri path];
-        // NSLog(@"url path: %@", path);
-        BOOL isDir = NO;
-        // see if exists and is file or dir
-        BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir];
-        if (bExists) {
-            // see if it contains docs path or temp path
-            NSString* foundFullPath = nil;
-            if ([path hasPrefix:self.appDocsPath]) {
-                foundFullPath = self.appDocsPath;
-            } else if ([path hasPrefix:self.appTempPath]) {
-                foundFullPath = self.appTempPath;
-            }
-
-            if (foundFullPath == nil) {
-                // error SECURITY_ERR - not one of the two paths types supported
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SECURITY_ERR];
-            } else {
-                NSDictionary* fileSystem = [self getDirectoryEntry:path isDirectory:isDir];
+    if ([[url.url scheme] isEqualToString:@"assets-library"]) {
+        NSDictionary* fileSystem = [self getDirectoryEntry:[url.url absoluteString] isDirectory:NO];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+    } else {
+        if (url.fileSystemType == -1) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+        } else {
+            NSFileManager* fileMgr = [[NSFileManager alloc] init];
+            BOOL isDir = NO;
+            // see if exists and is file or dir
+            BOOL bExists = [fileMgr fileExistsAtPath:url.fileSystemPath isDirectory:&isDir];
+            if (bExists) {
+                NSDictionary* fileSystem = [self makeEntryForPath:url.fullPath fileSystem:url.fileSystemType isDirectory:isDir];
                 result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+            } else {
+                // return NOT_FOUND_ERR
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
             }
-        } else {
-            // return NOT_FOUND_ERR
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
         }
-    } else if ([strUri hasPrefix:@"assets-library://"]) {
-        NSDictionary* fileSystem = [self getDirectoryEntry:strUri isDirectory:NO];
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
-    } else {
-        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
-    }
-
-    if (result != nil) {
-        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
     }
+    return result;
 }
 
 /* Part of DirectoryEntry interface,  creates or returns the specified directory
  * IN:
- *	NSString* fullPath - full path for this directory
+ *	NSString* localURI - local filesystem URI for this directory
  *	NSString* path - directory to be created/returned; may be full path or relative path
  *	NSDictionary* - Flags object
  *		boolean as NSNumber create -
@@ -310,9 +430,9 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 
 /* Part of DirectoryEntry interface,  creates or returns the specified file
  * IN:
- *	NSString* fullPath - full path for this file
- *	NSString* path - file to be created/returned; may be full path or relative path
- *	NSDictionary* - Flags object
+ *	NSString* baseURI - local filesytem URI for the base directory to search
+ *	NSString* requestedPath - file to be created/returned; may be absolute path or relative path
+ *	NSDictionary* options - Flags object
  *		boolean as NSNumber create -
  *			if create is true and file does not exist, create file and return File entry
  *			if create is true and exclusive is true and file does exist, return error
@@ -320,18 +440,16 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
  *			if create is false and the path represents a directory, return error
  *		boolean as NSNumber exclusive - used in conjunction with create
  *			if exclusive is true and create is true - specifies failure if file already exists
- *
- *
  */
 - (void)getFile:(CDVInvokedUrlCommand*)command
 {
-    // arguments are URL encoded
-    NSString* fullPath = [command.arguments objectAtIndex:0];
+    NSString* baseURIstr = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* baseURI = [self fileSystemURLWithString:baseURIstr];
     NSString* requestedPath = [command.arguments objectAtIndex:1];
     NSDictionary* options = [command.arguments objectAtIndex:2 withDefault:nil];
 
     // return unsupported result for assets-library URLs
-    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if ([[baseURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
         CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
         return;
@@ -358,22 +476,12 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
     if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
         errorCode = ENCODING_ERR;
     } else {
-        // was full or relative path provided?
-        NSRange range = [requestedPath rangeOfString:fullPath];
-        BOOL bIsFullPath = range.location != NSNotFound;
-
-        NSString* reqFullPath = nil;
-
-        if (!bIsFullPath) {
-            reqFullPath = [fullPath stringByAppendingPathComponent:requestedPath];
-        } else {
-            reqFullPath = requestedPath;
-        }
-
+        CDVFilesystemURL* requestedURL = [self fileSystemURLWithURL:[NSURL URLWithString:[requestedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding] relativeToURL:baseURI.url]]; /* TODO: UGLY - FIX */
+        
         // NSLog(@"reqFullPath = %@", reqFullPath);
         NSFileManager* fileMgr = [[NSFileManager alloc] init];
         BOOL bIsDir;
-        BOOL bExists = [fileMgr fileExistsAtPath:reqFullPath isDirectory:&bIsDir];
+        BOOL bExists = [fileMgr fileExistsAtPath:requestedURL.fileSystemPath isDirectory:&bIsDir];
         if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
             // path exists and is of requested type  - return TYPE_MISMATCH_ERR
             errorCode = TYPE_MISMATCH_ERR;
@@ -392,10 +500,10 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
             if (!bExists && (create == YES)) {
                 if (bDirRequest) {
                     // create the dir
-                    bSuccess = [fileMgr createDirectoryAtPath:reqFullPath withIntermediateDirectories:NO attributes:nil error:&pError];
+                    bSuccess = [fileMgr createDirectoryAtPath:requestedURL.fileSystemPath withIntermediateDirectories:NO attributes:nil error:&pError];
                 } else {
                     // create the empty file
-                    bSuccess = [fileMgr createFileAtPath:reqFullPath contents:nil attributes:nil];
+                    bSuccess = [fileMgr createFileAtPath:requestedURL.fileSystemPath contents:nil attributes:nil];
                 }
             }
             if (!bSuccess) {
@@ -406,7 +514,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
             } else {
                 // NSLog(@"newly created file/dir (%@) exists: %d", reqFullPath, [fileMgr fileExistsAtPath:reqFullPath]);
                 // file existed or was created
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:reqFullPath isDirectory:bDirRequest]];
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:requestedURL.fullPath fileSystem:baseURI.fileSystemType isDirectory:bDirRequest]];
             }
         } // are all possible conditions met?
     }
@@ -424,44 +532,36 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
  * If this Entry is the root of its filesystem, its parent is itself.
  * IN:
  * NSArray* arguments
- *	0 - NSString* fullPath
+ *	0 - NSString* localURI
  * NSMutableDictionary* options
  *	empty
  */
 - (void)getParent:(CDVInvokedUrlCommand*)command
 {
     // arguments are URL encoded
-    NSString* fullPath = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
 
     // we don't (yet?) support getting the parent of an asset
-    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
         CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_READABLE_ERR];
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
         return;
     }
 
     CDVPluginResult* result = nil;
-    NSString* newPath = nil;
-
-    if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+    CDVFilesystemURL *newURI = nil;
+    if ([localURI.fullPath isEqualToString:@""]) {
         // return self
-        newPath = fullPath;
+        newURI = localURI;
     } else {
-        // since this call is made from an existing Entry object - the parent should already exist so no additional error checking
-        // remove last component and return Entry
-        NSRange range = [fullPath rangeOfString:@"/" options:NSBackwardsSearch];
-        newPath = [fullPath substringToIndex:range.location];
-    }
-
-    if (newPath) {
-        NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        BOOL bIsDir;
-        BOOL bExists = [fileMgr fileExistsAtPath:newPath isDirectory:&bIsDir];
-        if (bExists) {
-            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:newPath isDirectory:bIsDir]];
-        }
+        newURI = [self fileSystemURLWithURL:[localURI.url URLByDeletingLastPathComponent]]; /* TODO: UGLY - FIX */
     }
-    if (!result) {
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    BOOL bIsDir;
+    BOOL bExists = [fileMgr fileExistsAtPath:newURI.fileSystemPath isDirectory:&bIsDir];
+    if (bExists) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self makeEntryForPath:newURI.fullPath fileSystem:newURI.fileSystemType isDirectory:bIsDir]];
+    } else {
         // invalid path or file does not exist
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
     }
@@ -475,10 +575,12 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 - (void)getMetadata:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* argPath = [command.arguments objectAtIndex:0];
+    NSString* localURIstr = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:localURIstr];
+    
     __block CDVPluginResult* result = nil;
 
-    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
         // In this case, we need to use an asynchronous method to retrieve the file.
         // Because of this, we can't just assign to `result` and send it at the end of the method.
         // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
@@ -502,16 +604,14 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
         };
 
         ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
-        [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock];
+        [assetsLibrary assetForURL:[NSURL URLWithString:localURIstr] resultBlock:resultBlock failureBlock:failureBlock];
         return;
     }
 
-    NSString* testPath = argPath; // [self getFullPath: argPath];
-
     NSFileManager* fileMgr = [[NSFileManager alloc] init];
     NSError* __autoreleasing error = nil;
 
-    NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:testPath error:&error];
+    NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:localURI.fileSystemPath error:&error];
 
     if (fileAttribs) {
         NSDate* modDate = [fileAttribs fileModificationDate];
@@ -542,13 +642,15 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 - (void)setMetadata:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* filePath = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
     NSDictionary* options = [command.arguments objectAtIndex:1 withDefault:nil];
+
     CDVPluginResult* result = nil;
     BOOL ok = NO;
 
     // setMetadata doesn't make sense for asset library files
-    if (![filePath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if (![[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+        NSString* filePath = localURI.fileSystemPath;
         // we only care about this iCloud key for now.
         // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
         NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
@@ -582,7 +684,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 /* removes the directory or file entry
  * IN:
  * NSArray* arguments
- *	0 - NSString* fullPath
+ *	0 - NSString* localURI
  *
  * returns NO_MODIFICATION_ALLOWED_ERR  if is top level directory or no permission to delete dir
  * returns INVALID_MODIFICATION_ERR if is non-empty dir or asset library file
@@ -591,24 +693,24 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 - (void)remove:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* fullPath = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
     CDVPluginResult* result = nil;
     CDVFileError errorCode = 0;  // !! 0 not currently defined
 
     // return error for assets-library URLs
-    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
         errorCode = INVALID_MODIFICATION_ERR;
-    } else if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+    } else if ([localURI.fullPath isEqualToString:@""]) {
         // error if try to remove top level (documents or tmp) dir
         errorCode = NO_MODIFICATION_ALLOWED_ERR;
     } else {
         NSFileManager* fileMgr = [[NSFileManager alloc] init];
         BOOL bIsDir = NO;
-        BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir];
+        BOOL bExists = [fileMgr fileExistsAtPath:localURI.fileSystemPath isDirectory:&bIsDir];
         if (!bExists) {
             errorCode = NOT_FOUND_ERR;
         }
-        if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fullPath error:nil] count] != 0)) {
+        if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:localURI.fileSystemPath error:nil] count] != 0)) {
             // dir is not empty
             errorCode = INVALID_MODIFICATION_ERR;
         }
@@ -617,7 +719,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
     } else {
         // perform actual remove
-        result = [self doRemove:fullPath];
+        result = [self doRemove:localURI.fileSystemPath];
     }
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
@@ -625,7 +727,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 /* recursively removes the directory
  * IN:
  * NSArray* arguments
- *	0 - NSString* fullPath
+ *	0 - NSString* localURI
  *
  * returns NO_MODIFICATION_ALLOWED_ERR  if is top level directory or no permission to delete dir
  * returns NOT_FOUND_ERR if file or dir is not found
@@ -633,22 +735,21 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 - (void)removeRecursively:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* fullPath = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
 
     // return unsupported result for assets-library URLs
-    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
         CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
         return;
     }
 
     CDVPluginResult* result = nil;
-
     // error if try to remove top level (documents or tmp) dir
-    if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+    if ([localURI.fullPath isEqualToString:@""]) {
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
     } else {
-        result = [self doRemove:fullPath];
+        result = [self doRemove:localURI.fileSystemPath];
     }
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
@@ -674,7 +775,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
         } else {
             // see if we can give a useful error
             CDVFileError errorCode = ABORT_ERR;
-            NSLog(@"error getting metadata: %@", [pError localizedDescription]);
+            NSLog(@"error removing filesystem entry at %@: %@", fullPath, [pError localizedDescription]);
             if ([pError code] == NSFileNoSuchFileError) {
                 errorCode = NOT_FOUND_ERR;
             } else if ([pError code] == NSFileWriteNoPermissionError) {
@@ -732,44 +833,45 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 /* Copy/move a file or directory to a new location
  * IN:
  * NSArray* arguments
- *	0 - NSString* fullPath of entry
- *  1 - NSString* newName the new name of the entry, defaults to the current name
- *	NSMutableDictionary* options - DirectoryEntry to which to copy the entry
+ *	0 - NSString* URL of entry to copy
+ *  1 - NSString* URL of the directory into which to copy/move the entry
+ *  2 - Optionally, the new name of the entry, defaults to the current name
  *	BOOL - bCopy YES if copy, NO if move
- *
  */
 - (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy
 {
     NSArray* arguments = command.arguments;
 
     // arguments
-    NSString* srcFullPath = [arguments objectAtIndex:0];
-    NSString* destRootPath = [arguments objectAtIndex:1];
-    // optional argument
-    NSString* newName = ([arguments count] > 2) ? [arguments objectAtIndex:2] : [srcFullPath lastPathComponent];          // use last component from appPath if new name not provided
+    NSString* srcURLstr = [arguments objectAtIndex:0];
+    NSString* destURLstr = [arguments objectAtIndex:1];
 
     __block CDVPluginResult* result = nil;
     CDVFileError errCode = 0;  // !! Currently 0 is not defined, use this to signal error !!
 
-    /*NSString* destRootPath = nil;
-    NSString* key = @"fullPath";
-    if([options valueForKeyIsString:key]){
-       destRootPath = [options objectForKey:@"fullPath"];
-    }*/
-
-    if (!destRootPath) {
-        // no destination provided
+    if (!srcURLstr || !destURLstr) {
+        // either no source or no destination provided
         errCode = NOT_FOUND_ERR;
-    } else if ([newName rangeOfString:@":"].location != NSNotFound) {
+    } else {
+    CDVFilesystemURL* srcURL = [self fileSystemURLWithString:srcURLstr];
+    CDVFilesystemURL* destURL = [self fileSystemURLWithString:destURLstr];
+    NSString* srcFullPath = srcURL.fileSystemPath;
+    NSString* destRootPath = destURL.fileSystemPath;
+    
+    // optional argument; use last component from srcFullPath if new name not provided
+    NSString* newName = ([arguments count] > 2) ? [arguments objectAtIndex:2] : [srcFullPath lastPathComponent];
+    if ([newName rangeOfString:@":"].location != NSNotFound) {
         // invalid chars in new name
         errCode = ENCODING_ERR;
     } else {
-        NSString* newFullPath = [destRootPath stringByAppendingPathComponent:newName];
+        NSString* newFileSystemPath = [destRootPath stringByAppendingPathComponent:newName];
+        NSString* newFullPath = [self fullPathForFileSystemPath:newFileSystemPath];
+        
         NSFileManager* fileMgr = [[NSFileManager alloc] init];
-        if ([newFullPath isEqualToString:srcFullPath]) {
+        if ([newFileSystemPath isEqualToString:srcFullPath]) {
             // source and destination can not be the same
             errCode = INVALID_MODIFICATION_ERR;
-        } else if ([srcFullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        } else if ([[srcURL.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
             if (bCopy) {
                 // Copying (as opposed to moving) an assets library file is okay.
                 // In this case, we need to use an asynchronous method to retrieve the file.
@@ -783,7 +885,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
                             result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
                             [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
                             return;
-                        } else if ([fileMgr fileExistsAtPath:newFullPath]) {
+                        } else if ([fileMgr fileExistsAtPath:newFileSystemPath]) {
                             // A file already exists at the destination path.
                             result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:PATH_EXISTS_ERR];
                             [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
@@ -795,8 +897,8 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
                         Byte* buffer = (Byte*)malloc([assetRepresentation size]);
                         NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
                         NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
-                        [data writeToFile:newFullPath atomically:YES];
-                        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:newFullPath isDirectory:NO]];
+                        [data writeToFile:newFileSystemPath atomically:YES];
+                        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:newFileSystemPath isDirectory:NO]];
                         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
                     } else {
                         // We couldn't find the asset.  Send the appropriate error.
@@ -823,7 +925,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
             BOOL bNewIsDir = NO;
             BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
             BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
-            BOOL bNewExists = [fileMgr fileExistsAtPath:newFullPath isDirectory:&bNewIsDir];
+            BOOL bNewExists = [fileMgr fileExistsAtPath:newFileSystemPath isDirectory:&bNewIsDir];
             if (!bSrcExists || !bDestExists) {
                 // the source or the destination root does not exist
                 errCode = NOT_FOUND_ERR;
@@ -834,14 +936,14 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
                 NSError* __autoreleasing error = nil;
                 BOOL bSuccess = NO;
                 if (bCopy) {
-                    if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFullPath] /*[newFullPath hasPrefix:srcFullPath]*/) {
+                    if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
                         // can't copy dir into self
                         errCode = INVALID_MODIFICATION_ERR;
                     } else if (bNewExists) {
                         // the full destination should NOT already exist if a copy
                         errCode = PATH_EXISTS_ERR;
                     } else {
-                        bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFullPath error:&error];
+                        bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
                     }
                 } else { // move
                     // iOS requires that destination must not exist before calling moveTo
@@ -850,35 +952,35 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
                     if (!bSrcIsDir && (bNewExists && bNewIsDir)) {
                         // can't move a file to directory
                         errCode = INVALID_MODIFICATION_ERR;
-                    } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFullPath]) {    // [newFullPath hasPrefix:srcFullPath]){
+                    } else if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFileSystemPath]) {
                         // can't move a dir into itself
                         errCode = INVALID_MODIFICATION_ERR;
                     } else if (bNewExists) {
-                        if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFullPath error:NULL] count] != 0)) {
+                        if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFileSystemPath error:NULL] count] != 0)) {
                             // can't move dir to a dir that is not empty
                             errCode = INVALID_MODIFICATION_ERR;
-                            newFullPath = nil;  // so we won't try to move
+                            newFileSystemPath = nil;  // so we won't try to move
                         } else {
                             // remove destination so can perform the moveItemAtPath
-                            bSuccess = [fileMgr removeItemAtPath:newFullPath error:NULL];
+                            bSuccess = [fileMgr removeItemAtPath:newFileSystemPath error:NULL];
                             if (!bSuccess) {
                                 errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
-                                newFullPath = nil;
+                                newFileSystemPath = nil;
                             }
                         }
-                    } else if (bNewIsDir && [newFullPath hasPrefix:srcFullPath]) {
+                    } else if (bNewIsDir && [newFileSystemPath hasPrefix:srcFullPath]) {
                         // can't move a directory inside itself or to any child at any depth;
                         errCode = INVALID_MODIFICATION_ERR;
-                        newFullPath = nil;
+                        newFileSystemPath = nil;
                     }
 
-                    if (newFullPath != nil) {
-                        bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFullPath error:&error];
+                    if (newFileSystemPath != nil) {
+                        bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFileSystemPath error:&error];
                     }
                 }
                 if (bSuccess) {
                     // should verify it is there and of the correct type???
-                    NSDictionary* newEntry = [self getDirectoryEntry:newFullPath isDirectory:bSrcIsDir];  // should be the same type as source
+                    NSDictionary* newEntry = [self makeEntryForPath:newFullPath fileSystem:srcURL.fileSystemType isDirectory:bSrcIsDir];  // should be the same type as source
                     result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
                 } else {
                     errCode = INVALID_MODIFICATION_ERR; // catch all
@@ -895,112 +997,76 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
             }
         }
     }
+    }
     if (errCode > 0) {
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
     }
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
-/* return the URI to the entry
- * IN:
- * NSArray* arguments
- *	0 - NSString* fullPath of entry
- *	1 - desired mime type of entry - ignored - always returns file://
- */
-
-/*  Not needed since W3C toURI is synchronous.  Leaving code here for now in case W3C spec changes.....
-- (void) toURI:(CDVInvokedUrlCommand*)command
-{
-    NSString* callbackId = command.callbackId;
-    NSString* argPath = [command.arguments objectAtIndex:0];
-    PluginResult* result = nil;
-    NSString* jsString = nil;
-
-    NSString* fullPath = [self getFullPath: argPath];
-    if (fullPath) {
-        // do we need to make sure the file actually exists?
-        // create file uri
-        NSString* strUri = [fullPath stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
-        NSURL* fileUrl = [NSURL fileURLWithPath:strUri];
-        if (fileUrl) {
-            result = [PluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: [fileUrl absoluteString]];
-            jsString = [result toSuccessCallbackString:callbackId];
-        } // else NOT_FOUND_ERR
-    }
-    if(!jsString) {
-        // was error
-        result = [PluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt: NOT_FOUND_ERR cast:  @"window.localFileSystem._castError"];
-        jsString = [result toErrorCallbackString:callbackId];
-    }
-
-    [self writeJavascript:jsString];
-}*/
 - (void)getFileMetadata:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* argPath = [command.arguments objectAtIndex:0];
-
+    NSString* localURIstr = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:localURIstr];
+    
     __block CDVPluginResult* result = nil;
 
-    NSString* fullPath = argPath; // [self getFullPath: argPath];
-
-    if (fullPath) {
-        if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
-            // In this case, we need to use an asynchronous method to retrieve the file.
-            // Because of this, we can't just assign to `result` and send it at the end of the method.
-            // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
-            ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
-                if (asset) {
-                    // We have the asset!  Populate the dictionary and send it off.
-                    NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
-                    ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
-                    [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
-                    [fileInfo setObject:argPath forKey:@"fullPath"];
-                    NSString* filename = [assetRepresentation filename];
-                    [fileInfo setObject:filename forKey:@"name"];
-                    [fileInfo setObject:[self getMimeTypeFromPath:filename] forKey:@"type"];
-                    NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
-                    NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
-                    [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
-
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
-                    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-                } else {
-                    // We couldn't find the asset.  Send the appropriate error.
-                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-                    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-                }
-            };
-            ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
-                // Retrieving the asset failed for some reason.  Send the appropriate error.
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
-                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
-            };
-
-            ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
-            [assetsLibrary assetForURL:[NSURL URLWithString:argPath] resultBlock:resultBlock failureBlock:failureBlock];
-            return;
-        } else {
-            NSFileManager* fileMgr = [[NSFileManager alloc] init];
-            BOOL bIsDir = NO;
-            // make sure it exists and is not a directory
-            BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir];
-            if (!bExists || bIsDir) {
-                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
-            } else {
-                // create dictionary of file info
-                NSError* __autoreleasing error = nil;
-                NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:fullPath error:&error];
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
+        // In this case, we need to use an asynchronous method to retrieve the file.
+        // Because of this, we can't just assign to `result` and send it at the end of the method.
+        // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+        ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset* asset) {
+            if (asset) {
+                // We have the asset!  Populate the dictionary and send it off.
                 NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
-                [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
-                [fileInfo setObject:argPath forKey:@"fullPath"];
-                [fileInfo setObject:@"" forKey:@"type"];  // can't easily get the mimetype unless create URL, send request and read response so skipping
-                [fileInfo setObject:[argPath lastPathComponent] forKey:@"name"];
-                NSDate* modDate = [fileAttrs fileModificationDate];
-                NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
+                ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+                [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[assetRepresentation size]] forKey:@"size"];
+                [fileInfo setObject:localURIstr forKey:@"fullPath"];
+                NSString* filename = [assetRepresentation filename];
+                [fileInfo setObject:filename forKey:@"name"];
+                [fileInfo setObject:[self getMimeTypeFromPath:filename] forKey:@"type"];
+                NSDate* creationDate = [asset valueForProperty:ALAssetPropertyDate];
+                NSNumber* msDate = [NSNumber numberWithDouble:[creationDate timeIntervalSince1970] * 1000];
                 [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+                
                 result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+            } else {
+                // We couldn't find the asset.  Send the appropriate error.
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+                [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
             }
+        };
+        ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError* error) {
+            // Retrieving the asset failed for some reason.  Send the appropriate error.
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        };
+        
+        ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+        [assetsLibrary assetForURL:[NSURL URLWithString:localURIstr] resultBlock:resultBlock failureBlock:failureBlock];
+        return;
+    } else {
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir = NO;
+        // make sure it exists and is not a directory
+        BOOL bExists = [fileMgr fileExistsAtPath:localURI.fileSystemPath isDirectory:&bIsDir];
+        if (!bExists || bIsDir) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+        } else {
+            // create dictionary of file info
+            NSError* __autoreleasing error = nil;
+            NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:localURI.fileSystemPath error:&error];
+            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
+            [fileInfo setObject:localURIstr forKey:@"fullPath"];
+            [fileInfo setObject:@"" forKey:@"type"];  // can't easily get the mimetype unless create URL, send request and read response so skipping
+            [fileInfo setObject:[localURIstr lastPathComponent] forKey:@"name"];
+            NSDate* modDate = [fileAttrs fileModificationDate];
+            NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
+            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
         }
     }
     if (!result) {
@@ -1011,21 +1077,19 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 
 - (void)readEntries:(CDVInvokedUrlCommand*)command
 {
-    // arguments
-    NSString* fullPath = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command.arguments objectAtIndex:0]];
+    CDVPluginResult *result;
 
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
     // return unsupported result for assets-library URLs
-    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
-        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
         return;
     }
 
-    CDVPluginResult* result = nil;
-
     NSFileManager* fileMgr = [[NSFileManager alloc] init];
     NSError* __autoreleasing error = nil;
-    NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fullPath error:&error];
+    NSArray* contents = [fileMgr contentsOfDirectoryAtPath:localURI.fileSystemPath error:&error];
 
     if (contents) {
         NSMutableArray* entries = [NSMutableArray arrayWithCapacity:1];
@@ -1033,10 +1097,10 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
             // create an Entry (as JSON) for each file/dir
             for (NSString* name in contents) {
                 // see if is dir or file
-                NSString* entryPath = [fullPath stringByAppendingPathComponent:name];
+                NSString* entryPath = [localURI.fileSystemPath stringByAppendingPathComponent:name];
                 BOOL bIsDir = NO;
                 [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir];
-                NSDictionary* entryDict = [self getDirectoryEntry:entryPath isDirectory:bIsDir];
+                NSDictionary* entryDict = [self makeEntryForPath:[self fullPathForFileSystemPath:entryPath] fileSystem:localURI.fileSystemType isDirectory:bIsDir];
                 [entries addObject:entryDict];
             }
         }
@@ -1045,7 +1109,6 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
         // assume not found but could check error for more specific error conditions
         result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
     }
-
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 }
 
@@ -1067,7 +1130,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
                         NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
                         NSData* data = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
                         NSString* MIMEType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)[assetRepresentation UTI], kUTTagClassMIMEType);
-
+                        
                         callback(data, MIMEType, NO_ERROR);
                     } else {
                         callback(nil, nil, NOT_FOUND_ERR);
@@ -1078,7 +1141,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
                     NSLog(@"Error: %@", error);
                     callback(nil, nil, SECURITY_ERR);
                 };
-
+                
                 ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
                 [assetsLibrary assetForURL:[NSURL URLWithString:path] resultBlock:resultBlock failureBlock:failureBlock];
             } else {
@@ -1090,16 +1153,16 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
                 if (start > 0) {
                     [file seekToFileOffset:start];
                 }
-
+                
                 NSData* readData;
                 if (end < 0) {
                     readData = [file readDataToEndOfFile];
                 } else {
                     readData = [file readDataOfLength:(end - start)];
                 }
-
+                
                 [file closeFile];
-
+                
                 callback(readData, mimeType, readData != nil ? NO_ERROR : NOT_FOUND_ERR);
             }
         }];
@@ -1117,7 +1180,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 - (void)readAsText:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* path = [command argumentAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command argumentAtIndex:0]];
     NSString* encoding = [command argumentAtIndex:1];
     NSInteger start = [[command argumentAtIndex:2] integerValue];
     NSInteger end = [[command argumentAtIndex:3] integerValue];
@@ -1130,7 +1193,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
         return;
     }
 
-    [self readFileWithPath:path start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+    [self readFileWithPath:localURI.fileSystemPath start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
         CDVPluginResult* result = nil;
         if (data != nil) {
             NSString* str = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSUTF8StringEncoding freeWhenDone:NO];
@@ -1162,11 +1225,11 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 
 - (void)readAsDataURL:(CDVInvokedUrlCommand*)command
 {
-    NSString* path = [command argumentAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command argumentAtIndex:0]];
     NSInteger start = [[command argumentAtIndex:1] integerValue];
     NSInteger end = [[command argumentAtIndex:2] integerValue];
 
-    [self readFileWithPath:path start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+    [self readFileWithPath:localURI.fileSystemPath start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
         CDVPluginResult* result = nil;
         if (data != nil) {
             // TODO: Would be faster to base64 encode directly to the final string.
@@ -1190,11 +1253,11 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 
 - (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command
 {
-    NSString* path = [command argumentAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command argumentAtIndex:0]];
     NSInteger start = [[command argumentAtIndex:1] integerValue];
     NSInteger end = [[command argumentAtIndex:2] integerValue];
 
-    [self readFileWithPath:path start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+    [self readFileWithPath:localURI.fileSystemPath start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
         CDVPluginResult* result = nil;
         if (data != nil) {
             result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArrayBuffer:data];
@@ -1208,11 +1271,11 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 
 - (void)readAsBinaryString:(CDVInvokedUrlCommand*)command
 {
-    NSString* path = [command argumentAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command argumentAtIndex:0]];
     NSInteger start = [[command argumentAtIndex:1] integerValue];
     NSInteger end = [[command argumentAtIndex:2] integerValue];
 
-    [self readFileWithPath:path start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+    [self readFileWithPath:localURI.fileSystemPath start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
         CDVPluginResult* result = nil;
         if (data != nil) {
             NSString* payload = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSASCIIStringEncoding freeWhenDone:NO];
@@ -1257,19 +1320,17 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 - (void)truncate:(CDVInvokedUrlCommand*)command
 {
     // arguments
-    NSString* argPath = [command.arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:[command argumentAtIndex:0]];
     unsigned long long pos = (unsigned long long)[[command.arguments objectAtIndex:1] longLongValue];
 
     // assets-library files can't be truncated
-    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
         CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
         return;
     }
 
-    NSString* appFile = argPath; // [self getFullPath:argPath];
-
-    unsigned long long newPos = [self truncateFile:appFile atPosition:pos];
+    unsigned long long newPos = [self truncateFile:localURI.fileSystemPath atPosition:pos];
     CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:newPos];
 
     [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
@@ -1293,7 +1354,7 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
 /* write
  * IN:
  * NSArray* arguments
- *  0 - NSString* file path to write to
+ *  0 - NSString* localURI of file to write to
  *  1 - NSString* or NSData* data to write
  *  2 - NSNumber* position to begin writing
  */
@@ -1303,25 +1364,24 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
     NSArray* arguments = command.arguments;
 
     // arguments
-    NSString* argPath = [arguments objectAtIndex:0];
+    NSString* localURIstr = [arguments objectAtIndex:0];
+    CDVFilesystemURL* localURI = [self fileSystemURLWithString:localURIstr];
     id argData = [arguments objectAtIndex:1];
     unsigned long long pos = (unsigned long long)[[arguments objectAtIndex:2] longLongValue];
 
     // text can't be written into assets-library files
-    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+    if ([[localURI.url scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
         CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
         [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
         return;
     }
 
-    NSString* fullPath = argPath; // [self getFullPath:argPath];
-
-    [self truncateFile:fullPath atPosition:pos];
+    [self truncateFile:localURI.fileSystemPath atPosition:pos];
 
     if ([argData isKindOfClass:[NSString class]]) {
-        [self writeToFile:fullPath withString:argData encoding:NSUTF8StringEncoding append:YES callback:callbackId];
+        [self writeToFile:localURI.fileSystemPath withString:argData encoding:NSUTF8StringEncoding append:YES callback:callbackId];
     } else if ([argData isKindOfClass:[NSData class]]) {
-        [self writeToFile:fullPath withData:argData append:YES callback:callbackId];
+        [self writeToFile:localURI.fileSystemPath withData:argData append:YES callback:callbackId];
     } else {
         CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid parameter type"];
         [self.commandDelegate sendPluginResult:result callbackId:callbackId];
@@ -1368,6 +1428,8 @@ NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
     [self writeToFile:filePath withData:[stringData dataUsingEncoding:encoding allowLossyConversion:YES] append:shouldAppend callback:callbackId];
 }
 
+#pragma mark Undocumented Filesystem API
+
 - (void)testFileExists:(CDVInvokedUrlCommand*)command
 {
     // arguments

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/b3b6a83f/www/ios/FileSystem.js
----------------------------------------------------------------------
diff --git a/www/ios/FileSystem.js b/www/ios/FileSystem.js
new file mode 100644
index 0000000..2017e29
--- /dev/null
+++ b/www/ios/FileSystem.js
@@ -0,0 +1,27 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+*/
+
+module.exports = {
+    __format__: function(fullPath) {
+        return 'filesystem://localhost/'+this.name+(fullPath[0]==='/'?'':'/')+encodeURI(fullPath);
+    }
+};
+