cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From shaz...@apache.org
Subject [3/4] cordova-plugin-file git commit: CB-9544 Add file plugin for OSX
Date Wed, 26 Aug 2015 22:59:46 GMT
CB-9544 Add file plugin for OSX

- adding osx specific sources
- adding osx to plugin.xml and package.json


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/c3b5778e
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/c3b5778e
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/c3b5778e

Branch: refs/heads/master
Commit: c3b5778ecb66a1252b2ab05e00e3c43ac28342f6
Parents: 11d6cf0
Author: Tobias Bocanegra <tripod@adobe.com>
Authored: Tue Aug 25 18:09:47 2015 -0700
Committer: Shazron Abdullah <shazron@apache.org>
Committed: Wed Aug 26 16:02:17 2015 -0700

----------------------------------------------------------------------
 README.md                    |    1 +
 package.json                 |    2 +
 plugin.xml                   |   28 +
 src/osx/CDVFile.h            |  157 ++++++
 src/osx/CDVFile.m            | 1099 +++++++++++++++++++++++++++++++++++++
 src/osx/CDVLocalFilesystem.h |   32 ++
 src/osx/CDVLocalFilesystem.m |  733 +++++++++++++++++++++++++
 tests/tests.js               |    1 +
 www/osx/FileSystem.js        |   30 +
 9 files changed, 2083 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c3b5778e/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 2495529..a62ac9f 100644
--- a/README.md
+++ b/README.md
@@ -63,6 +63,7 @@ Although in the global scope, it is not available until after the `deviceready`
 - Windows 8*
 - Windows*
 - Browser
+- OS X
 
 \* _These platforms do not support `FileReader.readAsArrayBuffer` nor `FileWriter.write(blob)`._
 

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c3b5778e/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index ad35dd1..b9aeead 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,7 @@
       "amazon-fireos",
       "ubuntu",
       "ios",
+      "osx",
       "wp7",
       "wp8",
       "blackberry10",
@@ -29,6 +30,7 @@
     "cordova-amazon-fireos",
     "cordova-ubuntu",
     "cordova-ios",
+    "cordova-osx",
     "cordova-wp7",
     "cordova-wp8",
     "cordova-blackberry10",

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c3b5778e/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 76be8bf..a138676 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -248,6 +248,34 @@ to config.xml in order for the application to find previously stored files.
         <framework src="MobileCoreServices.framework" />
     </platform>
 
+    <!-- osx -->
+    <platform name="osx">
+        <config-file target="config.xml" parent="/*">
+            <feature name="File">
+                <param name="ios-package" value="CDVFile" />
+                <param name="onload" value="true" />
+            </feature>
+        </config-file>
+        <header-file src="src/osx/CDVFile.h" />
+        <source-file src="src/osx/CDVFile.m" />
+        <header-file src="src/osx/CDVLocalFilesystem.h" />
+        <source-file src="src/osx/CDVLocalFilesystem.m" />
+
+        <!-- osx specific file apis -->
+        <js-module src="www/osx/FileSystem.js" name="osxFileSystem">
+            <merges target="FileSystem" />
+        </js-module>
+
+        <js-module src="www/fileSystems-roots.js" name="fileSystems-roots">
+            <runs/>
+        </js-module>
+
+        <js-module src="www/fileSystemPaths.js" name="fileSystemPaths">
+            <merges target="cordova" />
+            <runs/>
+        </js-module>
+    </platform>
+
     <!-- wp7 -->
     <platform name="wp7">
         <config-file target="config.xml" parent="/*">

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c3b5778e/src/osx/CDVFile.h
----------------------------------------------------------------------
diff --git a/src/osx/CDVFile.h b/src/osx/CDVFile.h
new file mode 100644
index 0000000..33630c0
--- /dev/null
+++ b/src/osx/CDVFile.h
@@ -0,0 +1,157 @@
+/*
+ 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 <Foundation/Foundation.h>
+#import <Cordova/CDVPlugin.h>
+
+NSString* const kCDVAssetsLibraryPrefix;
+NSString* const kCDVFilesystemURLPrefix;
+
+enum CDVFileError {
+    NO_ERROR = 0,
+    NOT_FOUND_ERR = 1,
+    SECURITY_ERR = 2,
+    ABORT_ERR = 3,
+    NOT_READABLE_ERR = 4,
+    ENCODING_ERR = 5,
+    NO_MODIFICATION_ALLOWED_ERR = 6,
+    INVALID_STATE_ERR = 7,
+    SYNTAX_ERR = 8,
+    INVALID_MODIFICATION_ERR = 9,
+    QUOTA_EXCEEDED_ERR = 10,
+    TYPE_MISMATCH_ERR = 11,
+    PATH_EXISTS_ERR = 12
+};
+typedef int CDVFileError;
+
+@interface CDVFilesystemURL : NSObject  {
+    NSURL *_url;
+    NSString *_fileSystemName;
+    NSString *_fullPath;
+}
+
+- (id) initWithString:(NSString*)strURL;
+- (id) initWithURL:(NSURL*)URL;
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL;
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL;
+
+- (NSString *)absoluteURL;
+
+@property (atomic) NSURL *url;
+@property (atomic) NSString *fileSystemName;
+@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;
+- (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;
+
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url;
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir;
+
+@property (nonatomic,strong) NSString *name;
+@property (nonatomic, copy) NSURL*(^urlTransformer)(NSURL*);
+
+@optional
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURI;
+- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path;
+
+@end
+
+@interface CDVFile : CDVPlugin {
+    NSString* rootDocsPath;
+    NSString* appDocsPath;
+    NSString* appLibraryPath;
+    NSString* appTempPath;
+
+    NSMutableArray* fileSystems_;
+    BOOL userHasAllowed;
+}
+
+- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath;
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir;
+- (NSDictionary *)makeEntryForURL:(NSURL *)URL;
+- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath;
+
+- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL;
+
+/* Native Registration API */
+- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs;
+- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName;
+
+/* Exec API */
+- (void)requestFileSystem:(CDVInvokedUrlCommand*)command;
+- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command;
+- (void)getDirectory:(CDVInvokedUrlCommand*)command;
+- (void)getFile:(CDVInvokedUrlCommand*)command;
+- (void)getParent:(CDVInvokedUrlCommand*)command;
+- (void)removeRecursively:(CDVInvokedUrlCommand*)command;
+- (void)remove:(CDVInvokedUrlCommand*)command;
+- (void)copyTo:(CDVInvokedUrlCommand*)command;
+- (void)moveTo:(CDVInvokedUrlCommand*)command;
+- (void)getFileMetadata:(CDVInvokedUrlCommand*)command;
+- (void)readEntries:(CDVInvokedUrlCommand*)command;
+- (void)readAsText:(CDVInvokedUrlCommand*)command;
+- (void)readAsDataURL:(CDVInvokedUrlCommand*)command;
+- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command;
+- (void)write:(CDVInvokedUrlCommand*)command;
+- (void)testFileExists:(CDVInvokedUrlCommand*)command;
+- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command;
+- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command;
+- (void)truncate:(CDVInvokedUrlCommand*)command;
+- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy;
+
+/* Compatibilty with older File API */
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
+- (NSDictionary *)getDirectoryEntry:(NSString *)target isDirectory:(BOOL)bDirRequest;
+
+/* Conversion between filesystem paths and URLs */
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)URL;
+
+/* Internal methods for testing */
+- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command;
+
+@property (nonatomic, strong) NSString* rootDocsPath;
+@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
+
+#define kW3FileTemporary @"temporary"
+#define kW3FilePersistent @"persistent"

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c3b5778e/src/osx/CDVFile.m
----------------------------------------------------------------------
diff --git a/src/osx/CDVFile.m b/src/osx/CDVFile.m
new file mode 100644
index 0000000..4f1587e
--- /dev/null
+++ b/src/osx/CDVFile.m
@@ -0,0 +1,1099 @@
+/*
+ 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 <objc/message.h>
+
+static NSString* toBase64(NSData* data) {
+    SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
+    SEL s2 = NSSelectorFromString(@"base64EncodedString");
+    SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
+
+    if ([data respondsToSelector:s1]) {
+        NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
+        return func(data, s1);
+    } else if ([data respondsToSelector:s2]) {
+        NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s2];
+        return func(data, s2);
+    } else if ([data respondsToSelector:s3]) {
+        NSString* (*func)(id, SEL, NSUInteger) = (void *)[data methodForSelector:s3];
+        return func(data, s3, 0);
+    } else {
+        return nil;
+    }
+}
+
+CDVFile *filePlugin = nil;
+
+extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
+
+#ifndef __IPHONE_5_1
+    NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
+#endif
+
+NSString* const kCDVFilesystemURLPrefix = @"cdvfile";
+
+@implementation CDVFilesystemURL
+@synthesize url=_url;
+@synthesize fileSystemName=_fileSystemName;
+@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;
+        _fileSystemName = [self filesystemNameForLocalURI:URL];
+        _fullPath = [self fullPathForLocalURI:URL];
+    }
+    return self;
+}
+
+/*
+ * IN
+ *  NSString localURI
+ * OUT
+ *  NSString FileSystem Name for this URI, or nil if it is not recognized.
+ */
+- (NSString *)filesystemNameForLocalURI:(NSURL *)uri
+{
+    if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
+        NSArray *pathComponents = [uri pathComponents];
+        if (pathComponents != nil && pathComponents.count > 1) {
+            return [pathComponents objectAtIndex:1];
+        }
+    }
+    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
+{
+    if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
+        NSString *path = [uri path];
+        if ([uri query]) {
+            path = [NSString stringWithFormat:@"%@?%@", path, [uri query]];
+        }
+        NSRange slashRange = [path rangeOfString:@"/" options:0 range:NSMakeRange(1, path.length-1)];
+        if (slashRange.location == NSNotFound) {
+            return @"";
+        }
+        return [path substringFromIndex:slashRange.location];
+    }
+    return nil;
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL
+{
+    return [[CDVFilesystemURL alloc] initWithString:strURL];
+}
+
++ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL
+{
+    return [[CDVFilesystemURL alloc] initWithURL:URL];
+}
+
+- (NSString *)absoluteURL
+{
+    return [NSString stringWithFormat:@"cdvfile://localhost/%@%@", self.fileSystemName, self.fullPath];
+}
+
+@end
+
+@implementation CDVFilesystemURLProtocol
+
++ (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
+{
+    CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithURL:[[self request] URL]];
+    NSObject<CDVFileSystem> *fs = [filePlugin filesystemForURL:url];
+    [fs readFileAtURL:url start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) {
+        NSMutableDictionary* responseHeaders = [[NSMutableDictionary alloc] init];
+        responseHeaders[@"Cache-Control"] = @"no-cache";
+
+        if (!error) {
+            responseHeaders[@"Content-Type"] = mimetype;
+            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
+            [[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.url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
+            [[self client] URLProtocol:self didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
+            [[self client] URLProtocolDidFinishLoading:self];
+        }
+    }];
+}
+
+- (void)stopLoading
+{}
+
+- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
+                  willCacheResponse:(NSCachedURLResponse*)cachedResponse {
+    return nil;
+}
+
+@end
+
+
+@implementation CDVFile
+
+@synthesize rootDocsPath, appDocsPath, appLibraryPath, appTempPath, userHasAllowed, fileSystems=fileSystems_;
+
+- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs {
+    __weak CDVFile* weakSelf = self;
+    SEL sel = NSSelectorFromString(@"urlTransformer");
+    // for backwards compatibility - we check if this property is there
+    // we create a wrapper block because the urlTransformer property
+    // on the commandDelegate might be set dynamically at a future time
+    // (and not dependent on plugin loading order)
+    if ([self.commandDelegate respondsToSelector:sel]) {
+        fs.urlTransformer = ^NSURL*(NSURL* urlToTransform) {
+            // grab the block from the commandDelegate
+            NSURL* (^urlTransformer)(NSURL*) = ((id(*)(id, SEL))objc_msgSend)(weakSelf.commandDelegate, sel);
+            // if block is not null, we call it
+            if (urlTransformer) {
+                return urlTransformer(urlToTransform);
+            } else { // else we return the same url
+                return urlToTransform;
+            }
+        };
+    }
+    [fileSystems_ addObject:fs];
+}
+
+- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName
+{
+    if (self.fileSystems != nil) {
+        for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
+            if ([fs.name isEqualToString:fsName]) {
+                return fs;
+            }
+        }
+    }
+    return nil;
+
+}
+
+- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL {
+    if (localURL.fileSystemName == nil) return nil;
+    @try {
+        return [self fileSystemByName:localURL.fileSystemName];
+    }
+    @catch (NSException *e) {
+        return nil;
+    }
+}
+
+- (NSArray *)getExtraFileSystemsPreference:(NSWindowController *)vc
+{
+    NSString *filesystemsStr = nil;
+    if([self.viewController isKindOfClass:[CDVViewController class]]) {
+        CDVViewController *vc = (CDVViewController *)self.viewController;
+        NSDictionary *settings = [vc settings];
+        filesystemsStr = [settings[@"osxextrafilesystems"] lowercaseString];
+    }
+    if (!filesystemsStr) {
+        filesystemsStr = @"library,library-nosync,documents,documents-nosync,cache,bundle,root";
+    }
+    return [filesystemsStr componentsSeparatedByString:@","];
+}
+
+- (void)makeNonSyncable:(NSString*)path {
+    [[NSFileManager defaultManager] createDirectoryAtPath:path
+              withIntermediateDirectories:YES
+                               attributes:nil
+                                    error:nil];
+    NSURL* url = [NSURL fileURLWithPath:path];
+    [url setResourceValue: [NSNumber numberWithBool: YES]
+                   forKey: NSURLIsExcludedFromBackupKey error:nil];
+
+}
+
+- (void)registerExtraFileSystems:(NSArray *)filesystems fromAvailableSet:(NSDictionary *)availableFileSystems
+{
+    NSMutableSet *installedFilesystems = [[NSMutableSet alloc] initWithCapacity:7];
+
+    /* Build non-syncable directories as necessary */
+    for (NSString *nonSyncFS in @[@"library-nosync", @"documents-nosync"]) {
+        if ([filesystems containsObject:nonSyncFS]) {
+            [self makeNonSyncable:availableFileSystems[nonSyncFS]];
+        }
+    }
+
+    /* Register filesystems in order */
+    for (NSString *fsName in filesystems) {
+        if (![installedFilesystems containsObject:fsName]) {
+            NSString *fsRoot = availableFileSystems[fsName];
+            if (fsRoot) {
+                [filePlugin registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:fsName root:fsRoot]];
+                [installedFilesystems addObject:fsName];
+            } else {
+                NSLog(@"Unrecognized extra filesystem identifier: %@", fsName);
+            }
+        }
+    }
+}
+
+- (NSDictionary *)getAvailableFileSystems
+{
+    NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
+    return @{
+        @"library": libPath,
+        @"library-nosync": [libPath stringByAppendingPathComponent:@"NoCloud"],
+        @"documents": docPath,
+        @"documents-nosync": [docPath stringByAppendingPathComponent:@"NoCloud"],
+        @"cache": [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0],
+        @"bundle": [[NSBundle mainBundle] bundlePath],
+        @"root": @"/"
+    };
+}
+
+- (void)pluginInitialize
+{
+    filePlugin = self;
+    [NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]];
+
+    fileSystems_ = [[NSMutableArray alloc] initWithCapacity:3];
+
+    // Get the Library directory path
+    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+    self.appLibraryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"files"];
+
+    // Get the Temporary directory path
+    self.appTempPath = [NSTemporaryDirectory()stringByStandardizingPath];   // remove trailing slash from NSTemporaryDirectory()
+
+    // Get the Documents directory path
+    paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+    self.rootDocsPath = [paths objectAtIndex:0];
+    self.appDocsPath = [self.rootDocsPath stringByAppendingPathComponent:@"files"];
+
+
+    NSString *location = nil;
+    if([self.viewController isKindOfClass:[CDVViewController class]]) {
+        CDVViewController *vc = (CDVViewController *)self.viewController;
+        NSMutableDictionary *settings = vc.settings;
+        location = [[settings objectForKey:@"iospersistentfilelocation"] lowercaseString];
+    }
+    if (location == nil) {
+        // Compatibilty by default (if the config preference is not set, or
+        // if we're not embedded in a CDVViewController somehow.)
+        location = @"compatibility";
+    }
+
+    NSError *error;
+    if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appTempPath
+                                  withIntermediateDirectories:YES
+                                                   attributes:nil
+                                                        error:&error]) {
+        [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:self.appTempPath]];
+    } else {
+        NSLog(@"Unable to create temporary directory: %@", error);
+    }
+    if ([location isEqualToString:@"library"]) {
+        if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appLibraryPath
+                                      withIntermediateDirectories:YES
+                                                       attributes:nil
+                                                            error:&error]) {
+            [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.appLibraryPath]];
+        } else {
+            NSLog(@"Unable to create library directory: %@", error);
+        }
+    } else if ([location isEqualToString:@"compatibility"]) {
+        /*
+         *  Fall-back to compatibility mode -- this is the logic implemented in
+         *  earlier versions of this plugin, and should be maintained here so
+         *  that apps which were originally deployed with older versions of the
+         *  plugin can continue to provide access to files stored under those
+         *  versions.
+         */
+        [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.rootDocsPath]];
+    } else {
+        NSAssert(false,
+            @"File plugin configuration error: Please set iosPersistentFileLocation in config.xml to one of \"library\" (for new applications) or \"compatibility\" (for compatibility with previous versions)");
+    }
+
+    [self registerExtraFileSystems:[self getExtraFileSystemsPreference:self.viewController]
+                  fromAvailableSet:[self getAvailableFileSystems]];
+
+}
+
+- (CDVFilesystemURL *)fileSystemURLforArg:(NSString *)urlArg
+{
+    CDVFilesystemURL* ret = nil;
+    if ([urlArg hasPrefix:@"file://"]) {
+        /* This looks like a file url. Get the path, and see if any handlers recognize it. */
+        NSURL *fileURL = [NSURL URLWithString:urlArg];
+        NSURL *resolvedFileURL = [fileURL URLByResolvingSymlinksInPath];
+        NSString *path = [resolvedFileURL path];
+        ret = [self fileSystemURLforLocalPath:path];
+    } else {
+        ret = [CDVFilesystemURL fileSystemURLWithString:urlArg];
+    }
+    return ret;
+}
+
+- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath
+{
+    CDVFilesystemURL *localURL = nil;
+    NSUInteger shortestFullPath = 0;
+
+    // Try all installed filesystems, in order. Return the most match url.
+    for (id object in self.fileSystems) {
+        if ([object respondsToSelector:@selector(URLforFilesystemPath:)]) {
+            CDVFilesystemURL *url = [object URLforFilesystemPath:localPath];
+            if (url){
+                // A shorter fullPath would imply that the filesystem is a better match for the local path
+                if (!localURL || ([[url fullPath] length] < shortestFullPath)) {
+                    localURL = url;
+                    shortestFullPath = [[url fullPath] length];
+                }
+            }
+        }
+    }
+    return localURL;
+}
+
+- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath
+{
+    NSFileManager* fMgr = [[NSFileManager alloc] init];
+
+    NSError* __autoreleasing pError = nil;
+
+    NSDictionary* pDict = [fMgr attributesOfFileSystemForPath:appPath error:&pError];
+    NSNumber* pNumAvail = (NSNumber*)[pDict objectForKey:NSFileSystemFreeSize];
+
+    return pNumAvail;
+}
+
+/* Request the File System info
+ *
+ * IN:
+ * arguments[0] - type (number as string)
+ *	TEMPORARY = 0, PERSISTENT = 1;
+ * arguments[1] - size
+ *
+ * OUT:
+ *	Dictionary representing FileSystem object
+ *		name - the human readable directory name
+ *		root = DirectoryEntry object
+ *			bool isDirectory
+ *			bool isFile
+ *			string name
+ *			string fullPath
+ *			fileSystem = FileSystem object - !! ignored because creates circular reference !!
+ */
+
+- (void)requestFileSystem:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* strType = [command argumentAtIndex:0];
+    unsigned long long size = [[command argumentAtIndex:1] longLongValue];
+
+    int type = [strType intValue];
+    CDVPluginResult* result = nil;
+
+    if (type > self.fileSystems.count) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
+        NSLog(@"No filesystem of type requested");
+    } else {
+        NSString* fullPath = @"/";
+        // check for avail space for size request
+        NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
+        // NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]);
+        if (pNumAvail && ([pNumAvail unsignedLongLongValue] < size)) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:QUOTA_EXCEEDED_ERR];
+        } else {
+            NSObject<CDVFileSystem> *rootFs = [self.fileSystems objectAtIndex:type];
+            if (rootFs == nil) {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
+                NSLog(@"No filesystem of type requested");
+            } else {
+                NSMutableDictionary* fileSystem = [NSMutableDictionary dictionaryWithCapacity:2];
+                [fileSystem setObject:rootFs.name forKey:@"name"];
+                NSDictionary* dirEntry = [self makeEntryForPath:fullPath fileSystemName:rootFs.name isDirectory:YES];
+                [fileSystem setObject:dirEntry forKey:@"root"];
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+            }
+        }
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+
+- (void)requestAllFileSystems:(CDVInvokedUrlCommand*)command
+{
+    NSMutableArray* ret = [[NSMutableArray alloc] init];
+    for (NSObject<CDVFileSystem>* root in fileSystems_) {
+        [ret addObject:[self makeEntryForPath:@"/" fileSystemName:root.name isDirectory:YES]];
+    }
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:ret];
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)requestAllPaths:(CDVInvokedUrlCommand*)command
+{
+    NSString* libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0];
+    NSString* libPathSync = [libPath stringByAppendingPathComponent:@"Cloud"];
+    NSString* libPathNoSync = [libPath stringByAppendingPathComponent:@"NoCloud"];
+    NSString* docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+    NSString* storagePath = [libPath stringByDeletingLastPathComponent];
+    NSString* cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
+
+    // Create the directories if necessary.
+    [[NSFileManager defaultManager] createDirectoryAtPath:libPathSync withIntermediateDirectories:YES attributes:nil error:nil];
+    [[NSFileManager defaultManager] createDirectoryAtPath:libPathNoSync withIntermediateDirectories:YES attributes:nil error:nil];
+    // Mark NoSync as non-iCloud.
+    [[NSURL fileURLWithPath:libPathNoSync] setResourceValue: [NSNumber numberWithBool: YES]
+                                                     forKey: NSURLIsExcludedFromBackupKey error:nil];
+
+    NSDictionary* ret = @{
+        @"applicationDirectory": [[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]] absoluteString],
+        @"applicationStorageDirectory": [[NSURL fileURLWithPath:storagePath] absoluteString],
+        @"dataDirectory": [[NSURL fileURLWithPath:libPathNoSync] absoluteString],
+        @"syncedDataDirectory": [[NSURL fileURLWithPath:libPathSync] absoluteString],
+        @"documentsDirectory": [[NSURL fileURLWithPath:docPath] absoluteString],
+        @"cacheDirectory": [[NSURL fileURLWithPath:cachePath] absoluteString],
+        @"tempDirectory": [[NSURL fileURLWithPath:NSTemporaryDirectory()] absoluteString]
+    };
+
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:ret];
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* Creates and returns a dictionary representing an Entry Object
+ *
+ * IN:
+ * NSString* fullPath of the entry
+ * int fsType - FileSystem type
+ * BOOL isDirectory - YES if this is a directory, NO if is a file
+ * OUT:
+ * NSDictionary* Entry object
+ *		bool as NSNumber isDirectory
+ *		bool as NSNumber isFile
+ *		NSString*  name - last part of path
+ *		NSString* fullPath
+ *		NSString* filesystemName - FileSystem name -- actual filesystem will be created on the JS side if necessary, to avoid
+ *         creating circular reference (FileSystem contains DirectoryEntry which contains FileSystem.....!!)
+ */
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir
+{
+    NSObject<CDVFileSystem> *fs = [self fileSystemByName:fsName];
+    return [fs makeEntryForPath:fullPath isDirectory:isDir];
+}
+
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)localURL
+{
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURL];
+    return [fs makeEntryForLocalURL:localURL];
+}
+
+- (NSDictionary *)makeEntryForURL:(NSURL *)URL
+{
+    CDVFilesystemURL* fsURL = [self fileSystemURLforArg:[URL absoluteString]];
+    return [self makeEntryForLocalURL:fsURL];
+}
+
+/*
+ * Given a URI determine the File System information associated with it and return an appropriate W3C entry object
+ * IN
+ *	NSString* localURI: Should be an escaped local filesystem URI
+ * OUT
+ *	Entry object
+ *		bool isDirectory
+ *		bool isFile
+ *		string name
+ *		string fullPath
+ *		fileSystem = FileSystem object - !! ignored because creates circular reference FileSystem contains DirectoryEntry which contains FileSystem.....!!
+ */
+- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* localURIstr = [command argumentAtIndex:0];
+    CDVPluginResult* result;
+
+    localURIstr = [self encodePath:localURIstr]; //encode path before resolving
+    CDVFilesystemURL* inputURI = [self fileSystemURLforArg:localURIstr];
+
+    if (inputURI == nil || inputURI.fileSystemName == nil) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+    } else {
+        NSObject<CDVFileSystem> *fs = [self filesystemForURL:inputURI];
+        if (fs == nil) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+        } else {
+            result = [fs entryForLocalURI:inputURI];
+        }
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+//encode path with percent escapes
+-(NSString *)encodePath:(NSString *)path
+{
+    NSString *decodedPath = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //decode incase it's already encoded to avoid encoding twice
+    return [decodedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+}
+
+
+/* Part of DirectoryEntry interface,  creates or returns the specified directory
+ * IN:
+ *	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 -
+ *			if create is true and directory does not exist, create dir and return directory entry
+ *			if create is true and exclusive is true and directory does exist, return error
+ *			if create is false and directory does not exist, return error
+ *			if create is false and the path represents a file, return error
+ *		boolean as NSNumber exclusive - used in conjunction with create
+ *			if exclusive is true and create is true - specifies failure if directory already exists
+ *
+ *
+ */
+- (void)getDirectory:(CDVInvokedUrlCommand*)command
+{
+    NSMutableArray* arguments = [NSMutableArray arrayWithArray:command.arguments];
+    NSMutableDictionary* options = nil;
+
+    if ([arguments count] >= 3) {
+        options = [command argumentAtIndex:2 withDefault:nil];
+    }
+    // add getDir to options and call getFile()
+    if (options != nil) {
+        options = [NSMutableDictionary dictionaryWithDictionary:options];
+    } else {
+        options = [NSMutableDictionary dictionaryWithCapacity:1];
+    }
+    [options setObject:[NSNumber numberWithInt:1] forKey:@"getDir"];
+    if ([arguments count] >= 3) {
+        [arguments replaceObjectAtIndex:2 withObject:options];
+    } else {
+        [arguments addObject:options];
+    }
+    CDVInvokedUrlCommand* subCommand =
+        [[CDVInvokedUrlCommand alloc] initWithArguments:arguments
+                                             callbackId:command.callbackId
+                                              className:command.className
+                                             methodName:command.methodName];
+
+    [self getFile:subCommand];
+}
+
+/* Part of DirectoryEntry interface,  creates or returns the specified file
+ * IN:
+ *	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
+ *			if create is false and file does not exist, return error
+ *			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
+{
+    NSString* baseURIstr = [command argumentAtIndex:0];
+    CDVFilesystemURL* baseURI = [self fileSystemURLforArg:baseURIstr];
+    NSString* requestedPath = [command argumentAtIndex:1];
+    NSDictionary* options = [command argumentAtIndex:2 withDefault:nil];
+
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:baseURI];
+    CDVPluginResult* result = [fs getFileForURL:baseURI requestedPath:requestedPath options:options];
+
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * Look up the parent Entry containing this Entry.
+ * If this Entry is the root of its filesystem, its parent is itself.
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* localURI
+ * NSMutableDictionary* options
+ *	empty
+ */
+- (void)getParent:(CDVInvokedUrlCommand*)command
+{
+    // arguments are URL encoded
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+    CDVPluginResult* result = [fs getParentForURL:localURI];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * set MetaData of entry
+ * Currently we only support "com.apple.MobileBackup" (boolean)
+ */
+- (void)setMetadata:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    NSDictionary* options = [command argumentAtIndex:1 withDefault:nil];
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+    CDVPluginResult* result = [fs setMetadataForURL:localURI withObject:options];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* removes the directory or file entry
+ * IN:
+ * NSArray* arguments
+ *	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
+ * returns NOT_FOUND_ERR if file or dir is not found
+*/
+- (void)remove:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    CDVPluginResult* result = nil;
+
+    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 {
+        NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+        result = [fs removeFileAtURL:localURI];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* recursively removes the directory
+ * IN:
+ * NSArray* arguments
+ *	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
+ */
+- (void)removeRecursively:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    CDVPluginResult* result = nil;
+
+    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 {
+        NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+        result = [fs recursiveRemoveFileAtURL:localURI];
+    }
+    [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
+ * IN:
+ * NSArray* arguments
+ *	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* srcURLstr = [command argumentAtIndex:0];
+    NSString* destURLstr = [command argumentAtIndex:1];
+
+    CDVPluginResult *result;
+
+    if (!srcURLstr || !destURLstr) {
+        // either no source or no destination provided
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVFilesystemURL* srcURL = [self fileSystemURLforArg:srcURLstr];
+    CDVFilesystemURL* destURL = [self fileSystemURLforArg:destURLstr];
+
+    NSObject<CDVFileSystem> *srcFs = [self filesystemForURL:srcURL];
+    NSObject<CDVFileSystem> *destFs = [self filesystemForURL:destURL];
+
+    // optional argument; use last component from srcFullPath if new name not provided
+    NSString* newName = ([arguments count] > 2) ? [command argumentAtIndex:2] : [srcURL.url lastPathComponent];
+    if ([newName rangeOfString:@":"].location != NSNotFound) {
+        // invalid chars in new name
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    [destFs copyFileToURL:destURL withName:newName fromFileSystem:srcFs atURL:srcURL copy:bCopy callback:^(CDVPluginResult* result) {
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    }];
+
+}
+
+- (void)getFileMetadata:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+    [fs getFileMetadataForURL:localURI callback:^(CDVPluginResult* result) {
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    }];
+}
+
+- (void)readEntries:(CDVInvokedUrlCommand*)command
+{
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+    CDVPluginResult *result = [fs readEntriesAtURL:localURI];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* read and return file data
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath
+ *	1 - NSString* encoding
+ *	2 - NSString* start
+ *	3 - NSString* end
+ */
+- (void)readAsText:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    NSString* encoding = [command argumentAtIndex:1];
+    NSInteger start = [[command argumentAtIndex:2] integerValue];
+    NSInteger end = [[command argumentAtIndex:3] integerValue];
+
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+    if (fs == nil) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    // TODO: implement
+    if ([@"UTF-8" caseInsensitiveCompare : encoding] != NSOrderedSame) {
+        NSLog(@"Only UTF-8 encodings are currently supported by readAsText");
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    [self.commandDelegate runInBackground:^ {
+        [fs readFileAtURL:localURI 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];
+                // Check that UTF8 conversion did not fail.
+                if (str != nil) {
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:str];
+                    result.associatedObject = data;
+                } else {
+                    errorCode = ENCODING_ERR;
+                }
+            }
+            if (result == nil) {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+            }
+
+            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        }];
+    }];
+}
+
+/* Read content of text file and return as base64 encoded data url.
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath
+ *	1 - NSString* start
+ *	2 - NSString* end
+ *
+ * Determines the mime type from the file extension, returns ENCODING_ERR if mimetype can not be determined.
+ */
+
+- (void)readAsDataURL:(CDVInvokedUrlCommand*)command
+{
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    NSInteger start = [[command argumentAtIndex:1] integerValue];
+    NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+    [self.commandDelegate runInBackground:^ {
+        [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+            CDVPluginResult* result = nil;
+            if (data != nil) {
+                NSString* b64Str = toBase64(data);
+                NSString* output = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, b64Str];
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:output];
+            } else {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+            }
+
+            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        }];
+    }];
+}
+
+/* Read content of text file and return as an arraybuffer
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath
+ *	1 - NSString* start
+ *	2 - NSString* end
+ */
+
+- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command
+{
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    NSInteger start = [[command argumentAtIndex:1] integerValue];
+    NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+    [self.commandDelegate runInBackground:^ {
+        [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
+            CDVPluginResult* result = nil;
+            if (data != nil) {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArrayBuffer:data];
+            } else {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+            }
+
+            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        }];
+    }];
+}
+
+- (void)readAsBinaryString:(CDVInvokedUrlCommand*)command
+{
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    NSInteger start = [[command argumentAtIndex:1] integerValue];
+    NSInteger end = [[command argumentAtIndex:2] integerValue];
+
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+    [self.commandDelegate runInBackground:^ {
+        [fs readFileAtURL:localURI 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];
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
+                result.associatedObject = data;
+            } else {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+            }
+
+            [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        }];
+    }];
+}
+
+
+- (void)truncate:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+    unsigned long long pos = (unsigned long long)[[command argumentAtIndex:1] longLongValue];
+
+    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+    CDVPluginResult *result = [fs truncateFileAtURL:localURI atPosition:pos];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* write
+ * IN:
+ * NSArray* arguments
+ *  0 - NSString* localURI of file to write to
+ *  1 - NSString* or NSData* data to write
+ *  2 - NSNumber* position to begin writing
+ */
+- (void)write:(CDVInvokedUrlCommand*)command
+{
+    [self.commandDelegate runInBackground:^ {
+        NSString* callbackId = command.callbackId;
+
+        // arguments
+        CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
+        id argData = [command argumentAtIndex:1];
+        unsigned long long pos = (unsigned long long)[[command argumentAtIndex:2] longLongValue];
+
+        NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
+
+
+        [fs truncateFileAtURL:localURI atPosition:pos];
+        CDVPluginResult *result;
+        if ([argData isKindOfClass:[NSString class]]) {
+            NSData *encData = [argData dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
+            result = [fs writeToFileAtURL:localURI withData:encData append:YES];
+        } else if ([argData isKindOfClass:[NSData class]]) {
+            result = [fs writeToFileAtURL:localURI withData:argData append:YES];
+        } else {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid parameter type"];
+        }
+        [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+    }];
+}
+
+#pragma mark Methods for converting between URLs and paths
+
+- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURL
+{
+    for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
+        if ([fs.name isEqualToString:localURL.fileSystemName]) {
+            if ([fs respondsToSelector:@selector(filesystemPathForURL:)]) {
+                return [fs filesystemPathForURL:localURL];
+            }
+        }
+    }
+    return nil;
+}
+
+#pragma mark Undocumented Filesystem API
+
+- (void)testFileExists:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command argumentAtIndex:0];
+
+    // Get the file manager
+    NSFileManager* fMgr = [NSFileManager defaultManager];
+    NSString* appFile = argPath; // [ self getFullPath: argPath];
+
+    BOOL bExists = [fMgr fileExistsAtPath:appFile];
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(bExists ? 1 : 0)];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command argumentAtIndex:0];
+
+    // Get the file manager
+    NSFileManager* fMgr = [[NSFileManager alloc] init];
+    NSString* appFile = argPath; // [self getFullPath: argPath];
+    BOOL bIsDir = NO;
+    BOOL bExists = [fMgr fileExistsAtPath:appFile isDirectory:&bIsDir];
+
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:((bExists && bIsDir) ? 1 : 0)];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+// Returns number of bytes available via callback
+- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command
+{
+    // no arguments
+
+    NSNumber* pNumAvail = [self checkFreeDiskSpace:self.appDocsPath];
+
+    NSString* strFreeSpace = [NSString stringWithFormat:@"%qu", [pNumAvail unsignedLongLongValue]];
+    // NSLog(@"Free space is %@", strFreeSpace );
+
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:strFreeSpace];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+#pragma mark Compatibility with older File API
+
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+    return [CDVLocalFilesystem getMimeTypeFromPath:fullPath];
+}
+
+- (NSDictionary *)getDirectoryEntry:(NSString *)localPath isDirectory:(BOOL)bDirRequest
+{
+    CDVFilesystemURL *localURL = [self fileSystemURLforLocalPath:localPath];
+    return [self makeEntryForPath:localURL.fullPath fileSystemName:localURL.fileSystemName isDirectory:bDirRequest];
+}
+
+#pragma mark Internal methods for testing
+// Internal methods for testing: Get the on-disk location of a local filesystem url.
+// [Currently used for testing file-transfer]
+
+- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command
+{
+    CDVFilesystemURL* localURL = [self fileSystemURLforArg:command.arguments[0]];
+
+    NSString* fsPath = [self filesystemPathForURL:localURL];
+    CDVPluginResult* result;
+    if (fsPath) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:fsPath];
+    } else {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot resolve URL to a file"];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c3b5778e/src/osx/CDVLocalFilesystem.h
----------------------------------------------------------------------
diff --git a/src/osx/CDVLocalFilesystem.h b/src/osx/CDVLocalFilesystem.h
new file mode 100644
index 0000000..a0186c8
--- /dev/null
+++ b/src/osx/CDVLocalFilesystem.h
@@ -0,0 +1,32 @@
+/*
+ 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 *_name;
+    NSString *_fsRoot;
+}
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot;
++ (NSString*)getMimeTypeFromPath:(NSString*)fullPath;
+
+@property (nonatomic,strong) NSString *fsRoot;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/c3b5778e/src/osx/CDVLocalFilesystem.m
----------------------------------------------------------------------
diff --git a/src/osx/CDVLocalFilesystem.m b/src/osx/CDVLocalFilesystem.m
new file mode 100644
index 0000000..016e399
--- /dev/null
+++ b/src/osx/CDVLocalFilesystem.m
@@ -0,0 +1,733 @@
+/*
+ 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 <sys/xattr.h>
+
+@implementation CDVLocalFilesystem
+@synthesize name=_name, fsRoot=_fsRoot, urlTransformer;
+
+- (id) initWithName:(NSString *)name root:(NSString *)fsRoot
+{
+    if (self) {
+        _name = name;
+        _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;
+    NSDictionary* entry = [self makeEntryForLocalURL:url];
+    if (entry) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:entry];
+    } else {
+        // return NOT_FOUND_ERR
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    return result;
+}
+- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)url {
+    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) {
+        return [self makeEntryForPath:url.fullPath isDirectory:isDir];
+    } else {
+        return nil;
+    }
+}
+- (NSDictionary*)makeEntryForPath:(NSString*)fullPath isDirectory:(BOOL)isDir
+{
+    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:5];
+    NSString* lastPart = [[self stripQueryParametersFromPath: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:self.name forKey: @"filesystemName"];
+
+    NSURL* nativeURL = [NSURL fileURLWithPath:[self filesystemPathForFullPath:fullPath]];
+    if (self.urlTransformer) {
+        nativeURL = self.urlTransformer(nativeURL);
+    }
+
+    dirEntry[@"nativeURL"] = [nativeURL absoluteString];
+
+    return dirEntry;
+}
+
+- (NSString *)stripQueryParametersFromPath:(NSString *)fullPath
+{
+    NSRange questionMark = [fullPath rangeOfString:@"?"];
+    if (questionMark.location != NSNotFound) {
+        return [fullPath substringWithRange:NSMakeRange(0,questionMark.location)];
+    }
+    return fullPath;
+}
+
+- (NSString *)filesystemPathForFullPath:(NSString *)fullPath
+{
+    NSString *path = nil;
+    NSString *strippedFullPath = [self stripQueryParametersFromPath:fullPath];
+    path = [NSString stringWithFormat:@"%@%@", self.fsRoot, strippedFullPath];
+    if ([path length] > 1 && [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 *)filesystemPathForURL:(CDVFilesystemURL *)url
+{
+    return [self filesystemPathForFullPath:url.fullPath];
+}
+
+- (CDVFilesystemURL *)URLforFullPath:(NSString *)fullPath
+{
+    if (fullPath) {
+        NSString* escapedPath = [fullPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+        if ([fullPath hasPrefix:@"/"]) {
+            return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@%@", kCDVFilesystemURLPrefix, self.name, escapedPath]];
+        }
+        return [CDVFilesystemURL fileSystemURLWithString:[NSString stringWithFormat:@"%@://localhost/%@/%@", kCDVFilesystemURLPrefix, self.name, escapedPath]];
+    }
+    return nil;
+}
+
+- (CDVFilesystemURL *)URLforFilesystemPath:(NSString *)path
+{
+    return [self URLforFullPath:[self fullPathForFileSystemPath:path]];
+
+}
+
+- (NSString *)normalizePath:(NSString *)rawPath
+{
+    // If this is an absolute path, the first path component will be '/'. Skip it if that's the case
+    BOOL isAbsolutePath = [rawPath hasPrefix:@"/"];
+    if (isAbsolutePath) {
+        rawPath = [rawPath substringFromIndex:1];
+    }
+    NSMutableArray *components = [NSMutableArray arrayWithArray:[rawPath pathComponents]];
+    for (int index = 0; index < [components count]; ++index) {
+        if ([[components objectAtIndex:index] isEqualToString:@".."]) {
+            [components removeObjectAtIndex:index];
+            if (index > 0) {
+                [components removeObjectAtIndex:index-1];
+                --index;
+            }
+        }
+    }
+
+    if (isAbsolutePath) {
+        return [NSString stringWithFormat:@"/%@", [components componentsJoinedByString:@"/"]];
+    } else {
+        return [components componentsJoinedByString:@"/"];
+    }
+
+
+}
+
+- (BOOL)valueForKeyIsNumber:(NSDictionary*)dict key:(NSString*)key
+{
+    BOOL bNumber = NO;
+    NSObject* value = dict[key];
+    if (value) {
+        bNumber = [value isKindOfClass:[NSNumber class]];
+    }
+    return bNumber;
+}
+
+- (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 ([self valueForKeyIsNumber:options key:@"create"]) {
+        create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
+    }
+    if ([self valueForKeyIsNumber:options key:@"exclusive"]) {
+        exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
+    }
+    if ([self valueForKeyIsNumber:options key:@"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 {
+        // Build new fullPath for the requested resource.
+        // We concatenate the two paths together, and then scan the resulting string to remove
+        // parent ("..") references. Any parent references at the beginning of the string are
+        // silently removed.
+        NSString *combinedPath = [baseURI.fullPath stringByAppendingPathComponent:requestedPath];
+        combinedPath = [self normalizePath:combinedPath];
+        CDVFilesystemURL* requestedURL = [self URLforFullPath:combinedPath];
+
+        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 not 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 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 isDirectory:bIsDir]];
+    } else {
+        // invalid path or file does not exist
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    return 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]]) {
+// todo: fix me
+//        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] 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:(int)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];
+            if (len == 0) {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:(double)len];
+            } else {
+                [fileStream open];
+
+                bytesWritten = (int)[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 = [srcFs 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 isDirectory:bSrcIsDir];
+                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 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";
+                } else if ([[fullPath pathExtension] rangeOfString:@"css"].location != NSNotFound) {
+                    mimeType = @"text/css";
+                }
+            }
+            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];
+    CDVPluginResult *result;
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+    NSError* __autoreleasing error = nil;
+    NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:path error:&error];
+
+    if (fileAttrs) {
+
+        // create dictionary of file info
+        NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+
+        [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"];
+
+        // Ensure that directories (and other non-regular files) report size of 0
+        unsigned long long size = ([fileAttrs fileType] == NSFileTypeRegular ? [fileAttrs fileSize] : 0);
+        [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:size] forKey:@"size"];
+
+        NSDate* modDate = [fileAttrs fileModificationDate];
+        if (modDate) {
+            [fileInfo setObject:[NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000] forKey:@"lastModifiedDate"];
+        }
+
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+
+    } else {
+        // didn't get fileAttribs
+        CDVFileError errorCode = ABORT_ERR;
+        NSLog(@"error getting metadata: %@", [error localizedDescription]);
+        if ([error code] == NSFileNoSuchFileError || [error code] == NSFileReadNoSuchFileError) {
+            errorCode = NOT_FOUND_ERR;
+        }
+        // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
+    }
+
+    callback(result);
+}
+
+@end


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org


Mime
View raw message