arnaucube
arnaucube/openEventsPlatformAppPublic Self-hosted repository
openEventsPlatformApp/plugins/cordova-plugin-file/src/ios/CDVFile.m
plugins/cordova-plugin-file/src/ios/CDVFile.mRaw
   1/*
   2 Licensed to the Apache Software Foundation (ASF) under one
   3 or more contributor license agreements.  See the NOTICE file
   4 distributed with this work for additional information
   5 regarding copyright ownership.  The ASF licenses this file
   6 to you under the Apache License, Version 2.0 (the
   7 "License"); you may not use this file except in compliance
   8 with the License.  You may obtain a copy of the License at
   9
  10 http://www.apache.org/licenses/LICENSE-2.0
  11
  12 Unless required by applicable law or agreed to in writing,
  13 software distributed under the License is distributed on an
  14 "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15 KIND, either express or implied.  See the License for the
  16 specific language governing permissions and limitations
  17 under the License.
  18 */
  19
  20#import <Cordova/CDV.h>
  21#import "CDVFile.h"
  22#import "CDVLocalFilesystem.h"
  23#import "CDVAssetLibraryFilesystem.h"
  24#import <objc/message.h>
  25
  26static NSString* toBase64(NSData* data) {
  27    SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
  28    SEL s2 = NSSelectorFromString(@"base64EncodedString");
  29    SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
  30    
  31    if ([data respondsToSelector:s1]) {
  32        NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
  33        return func(data, s1);
  34    } else if ([data respondsToSelector:s2]) {
  35        NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s2];
  36        return func(data, s2);
  37    } else if ([data respondsToSelector:s3]) {
  38        NSString* (*func)(id, SEL, NSUInteger) = (void *)[data methodForSelector:s3];
  39        return func(data, s3, 0);
  40    } else {
  41        return nil;
  42    }
  43}
  44
  45CDVFile *filePlugin = nil;
  46
  47extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
  48
  49#ifndef __IPHONE_5_1
  50    NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
  51#endif
  52
  53NSString* const kCDVFilesystemURLPrefix = @"cdvfile";
  54
  55@implementation CDVFilesystemURL
  56@synthesize url=_url;
  57@synthesize fileSystemName=_fileSystemName;
  58@synthesize fullPath=_fullPath;
  59
  60- (id) initWithString:(NSString *)strURL
  61{
  62    if ( self = [super init] ) {
  63        NSURL *decodedURL = [NSURL URLWithString:strURL];
  64        return [self initWithURL:decodedURL];
  65    }
  66    return nil;
  67}
  68
  69-(id) initWithURL:(NSURL *)URL
  70{
  71    if ( self = [super init] ) {
  72        self.url = URL;
  73        self.fileSystemName = [self filesystemNameForLocalURI:URL];
  74        self.fullPath = [self fullPathForLocalURI:URL];
  75    }
  76    return self;
  77}
  78
  79/*
  80 * IN
  81 *  NSString localURI
  82 * OUT
  83 *  NSString FileSystem Name for this URI, or nil if it is not recognized.
  84 */
  85- (NSString *)filesystemNameForLocalURI:(NSURL *)uri
  86{
  87    if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
  88        NSArray *pathComponents = [uri pathComponents];
  89        if (pathComponents != nil && pathComponents.count > 1) {
  90            return [pathComponents objectAtIndex:1];
  91        }
  92    } else if ([[uri scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
  93        return @"assets-library";
  94    }
  95    return nil;
  96}
  97
  98/*
  99 * IN
 100 *  NSString localURI
 101 * OUT
 102 *  NSString fullPath component suitable for an Entry object.
 103 * The incoming URI should be properly escaped. The returned fullPath is unescaped.
 104 */
 105- (NSString *)fullPathForLocalURI:(NSURL *)uri
 106{
 107    if ([[uri scheme] isEqualToString:kCDVFilesystemURLPrefix] && [[uri host] isEqualToString:@"localhost"]) {
 108        NSString *path = [uri path];
 109        if ([uri query]) {
 110            path = [NSString stringWithFormat:@"%@?%@", path, [uri query]];
 111        }
 112        NSRange slashRange = [path rangeOfString:@"/" options:0 range:NSMakeRange(1, path.length-1)];
 113        if (slashRange.location == NSNotFound) {
 114            return @"";
 115        }
 116        return [path substringFromIndex:slashRange.location];
 117    } else if ([[uri scheme] isEqualToString:kCDVAssetsLibraryScheme]) {
 118        return [[uri absoluteString] substringFromIndex:[kCDVAssetsLibraryScheme length]+2];
 119    }
 120    return nil;
 121}
 122
 123+ (CDVFilesystemURL *)fileSystemURLWithString:(NSString *)strURL
 124{
 125    return [[CDVFilesystemURL alloc] initWithString:strURL];
 126}
 127
 128+ (CDVFilesystemURL *)fileSystemURLWithURL:(NSURL *)URL
 129{
 130    return [[CDVFilesystemURL alloc] initWithURL:URL];
 131}
 132
 133- (NSString *)absoluteURL
 134{
 135    return [NSString stringWithFormat:@"cdvfile://localhost/%@%@", self.fileSystemName, self.fullPath];
 136}
 137
 138@end
 139
 140@implementation CDVFilesystemURLProtocol
 141
 142+ (BOOL)canInitWithRequest:(NSURLRequest*)request
 143{
 144    NSURL* url = [request URL];
 145    return [[url scheme] isEqualToString:kCDVFilesystemURLPrefix];
 146}
 147
 148+ (NSURLRequest*)canonicalRequestForRequest:(NSURLRequest*)request
 149{
 150    return request;
 151}
 152
 153+ (BOOL)requestIsCacheEquivalent:(NSURLRequest*)requestA toRequest:(NSURLRequest*)requestB
 154{
 155    return [[[requestA URL] resourceSpecifier] isEqualToString:[[requestB URL] resourceSpecifier]];
 156}
 157
 158- (void)startLoading
 159{
 160    CDVFilesystemURL* url = [CDVFilesystemURL fileSystemURLWithURL:[[self request] URL]];
 161    NSObject<CDVFileSystem> *fs = [filePlugin filesystemForURL:url];
 162    __weak CDVFilesystemURLProtocol* weakSelf = self;
 163    
 164    [fs readFileAtURL:url start:0 end:-1 callback:^void(NSData *data, NSString *mimetype, CDVFileError error) {
 165        NSMutableDictionary* responseHeaders = [[NSMutableDictionary alloc] init];
 166        responseHeaders[@"Cache-Control"] = @"no-cache";
 167
 168        if (!error) {
 169            responseHeaders[@"Content-Type"] = mimetype;
 170            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:200 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
 171            [[weakSelf client] URLProtocol:weakSelf didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
 172            [[weakSelf client] URLProtocol:weakSelf didLoadData:data];
 173            [[weakSelf client] URLProtocolDidFinishLoading:weakSelf];
 174        } else {
 175            NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:url.url statusCode:404 HTTPVersion:@"HTTP/1.1"headerFields:responseHeaders];
 176            [[weakSelf client] URLProtocol:weakSelf didReceiveResponse:response cacheStoragePolicy:NSURLCacheStorageNotAllowed];
 177            [[weakSelf client] URLProtocolDidFinishLoading:weakSelf];
 178        }
 179    }];
 180}
 181
 182- (void)stopLoading
 183{}
 184
 185- (NSCachedURLResponse *)connection:(NSURLConnection *)connection
 186                  willCacheResponse:(NSCachedURLResponse*)cachedResponse {
 187    return nil;
 188}
 189
 190@end
 191
 192
 193@implementation CDVFile
 194
 195@synthesize rootDocsPath, appDocsPath, appLibraryPath, appTempPath, userHasAllowed, fileSystems=fileSystems_;
 196
 197- (void)registerFilesystem:(NSObject<CDVFileSystem> *)fs {
 198    __weak CDVFile* weakSelf = self;
 199    SEL sel = NSSelectorFromString(@"urlTransformer");
 200    // for backwards compatibility - we check if this property is there
 201    // we create a wrapper block because the urlTransformer property
 202    // on the commandDelegate might be set dynamically at a future time
 203    // (and not dependent on plugin loading order)
 204    if ([self.commandDelegate respondsToSelector:sel]) {
 205        fs.urlTransformer = ^NSURL*(NSURL* urlToTransform) {
 206            // grab the block from the commandDelegate
 207            NSURL* (^urlTransformer)(NSURL*) = ((id(*)(id, SEL))objc_msgSend)(weakSelf.commandDelegate, sel);
 208            // if block is not null, we call it
 209            if (urlTransformer) {
 210                return urlTransformer(urlToTransform);
 211            } else { // else we return the same url
 212                return urlToTransform;
 213            }
 214        };
 215    }
 216    [fileSystems_ addObject:fs];
 217}
 218
 219- (NSObject<CDVFileSystem> *)fileSystemByName:(NSString *)fsName
 220{
 221    if (self.fileSystems != nil) {
 222        for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
 223            if ([fs.name isEqualToString:fsName]) {
 224                return fs;
 225            }
 226        }
 227    }
 228    return nil;
 229}
 230
 231- (NSObject<CDVFileSystem> *)filesystemForURL:(CDVFilesystemURL *)localURL {
 232    if (localURL.fileSystemName == nil) return nil;
 233    @try {
 234        return [self fileSystemByName:localURL.fileSystemName];
 235    }
 236    @catch (NSException *e) {
 237        return nil;
 238    }
 239}
 240
 241- (NSArray *)getExtraFileSystemsPreference:(UIViewController *)vc
 242{
 243    NSString *filesystemsStr = nil;
 244    if([self.viewController isKindOfClass:[CDVViewController class]]) {
 245        CDVViewController *vc = (CDVViewController *)self.viewController;
 246        NSDictionary *settings = [vc settings];
 247        filesystemsStr = [settings[@"iosextrafilesystems"] lowercaseString];
 248    }
 249    if (!filesystemsStr) {
 250        filesystemsStr = @"library,library-nosync,documents,documents-nosync,cache,bundle,root";
 251    }
 252    return [filesystemsStr componentsSeparatedByString:@","];
 253}
 254
 255- (void)makeNonSyncable:(NSString*)path {
 256    [[NSFileManager defaultManager] createDirectoryAtPath:path
 257              withIntermediateDirectories:YES
 258                               attributes:nil
 259                                    error:nil];
 260    NSURL* url = [NSURL fileURLWithPath:path];
 261    [url setResourceValue: [NSNumber numberWithBool: YES]
 262                   forKey: NSURLIsExcludedFromBackupKey error:nil];
 263
 264}
 265
 266- (void)registerExtraFileSystems:(NSArray *)filesystems fromAvailableSet:(NSDictionary *)availableFileSystems
 267{
 268    NSMutableSet *installedFilesystems = [[NSMutableSet alloc] initWithCapacity:7];
 269
 270    /* Build non-syncable directories as necessary */
 271    for (NSString *nonSyncFS in @[@"library-nosync", @"documents-nosync"]) {
 272        if ([filesystems containsObject:nonSyncFS]) {
 273            [self makeNonSyncable:availableFileSystems[nonSyncFS]];
 274        }
 275    }
 276
 277    /* Register filesystems in order */
 278    for (NSString *fsName in filesystems) {
 279        if (![installedFilesystems containsObject:fsName]) {
 280            NSString *fsRoot = availableFileSystems[fsName];
 281            if (fsRoot) {
 282                [filePlugin registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:fsName root:fsRoot]];
 283                [installedFilesystems addObject:fsName];
 284            } else {
 285                NSLog(@"Unrecognized extra filesystem identifier: %@", fsName);
 286            }
 287        }
 288    }
 289}
 290
 291- (NSDictionary *)getAvailableFileSystems
 292{
 293    NSString *libPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
 294    NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
 295    return @{
 296        @"library": libPath,
 297        @"library-nosync": [libPath stringByAppendingPathComponent:@"NoCloud"],
 298        @"documents": docPath,
 299        @"documents-nosync": [docPath stringByAppendingPathComponent:@"NoCloud"],
 300        @"cache": [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0],
 301        @"bundle": [[NSBundle mainBundle] bundlePath],
 302        @"root": @"/"
 303    };
 304}
 305
 306- (void)pluginInitialize
 307{
 308    filePlugin = self;
 309    [NSURLProtocol registerClass:[CDVFilesystemURLProtocol class]];
 310
 311    fileSystems_ = [[NSMutableArray alloc] initWithCapacity:3];
 312
 313    // Get the Library directory path
 314    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
 315    self.appLibraryPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"files"];
 316
 317    // Get the Temporary directory path
 318    self.appTempPath = [NSTemporaryDirectory()stringByStandardizingPath];   // remove trailing slash from NSTemporaryDirectory()
 319
 320    // Get the Documents directory path
 321    paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
 322    self.rootDocsPath = [paths objectAtIndex:0];
 323    self.appDocsPath = [self.rootDocsPath stringByAppendingPathComponent:@"files"];
 324
 325
 326    NSString *location = nil;
 327    if([self.viewController isKindOfClass:[CDVViewController class]]) {
 328        CDVViewController *vc = (CDVViewController *)self.viewController;
 329        NSMutableDictionary *settings = vc.settings;
 330        location = [[settings objectForKey:@"iospersistentfilelocation"] lowercaseString];
 331    }
 332    if (location == nil) {
 333        // Compatibilty by default (if the config preference is not set, or
 334        // if we're not embedded in a CDVViewController somehow.)
 335        location = @"compatibility";
 336    }
 337
 338    NSError *error;
 339    if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appTempPath
 340                                  withIntermediateDirectories:YES
 341                                                   attributes:nil
 342                                                        error:&error]) {
 343        [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"temporary" root:self.appTempPath]];
 344    } else {
 345        NSLog(@"Unable to create temporary directory: %@", error);
 346    }
 347    if ([location isEqualToString:@"library"]) {
 348        if ([[NSFileManager defaultManager] createDirectoryAtPath:self.appLibraryPath
 349                                      withIntermediateDirectories:YES
 350                                                       attributes:nil
 351                                                            error:&error]) {
 352            [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.appLibraryPath]];
 353        } else {
 354            NSLog(@"Unable to create library directory: %@", error);
 355        }
 356    } else if ([location isEqualToString:@"compatibility"]) {
 357        /*
 358         *  Fall-back to compatibility mode -- this is the logic implemented in
 359         *  earlier versions of this plugin, and should be maintained here so
 360         *  that apps which were originally deployed with older versions of the
 361         *  plugin can continue to provide access to files stored under those
 362         *  versions.
 363         */
 364        [self registerFilesystem:[[CDVLocalFilesystem alloc] initWithName:@"persistent" root:self.rootDocsPath]];
 365    } else {
 366        NSAssert(false,
 367            @"File plugin configuration error: Please set iosPersistentFileLocation in config.xml to one of \"library\" (for new applications) or \"compatibility\" (for compatibility with previous versions)");
 368    }
 369    [self registerFilesystem:[[CDVAssetLibraryFilesystem alloc] initWithName:@"assets-library"]];
 370
 371    [self registerExtraFileSystems:[self getExtraFileSystemsPreference:self.viewController]
 372                  fromAvailableSet:[self getAvailableFileSystems]];
 373
 374}
 375
 376- (CDVFilesystemURL *)fileSystemURLforArg:(NSString *)urlArg
 377{
 378    CDVFilesystemURL* ret = nil;
 379    if ([urlArg hasPrefix:@"file://"]) {
 380        /* This looks like a file url. Get the path, and see if any handlers recognize it. */
 381        NSURL *fileURL = [NSURL URLWithString:urlArg];
 382        NSURL *resolvedFileURL = [fileURL URLByResolvingSymlinksInPath];
 383        NSString *path = [resolvedFileURL path];
 384        ret = [self fileSystemURLforLocalPath:path];
 385    } else {
 386        ret = [CDVFilesystemURL fileSystemURLWithString:urlArg];
 387    }
 388    return ret;
 389}
 390
 391- (CDVFilesystemURL *)fileSystemURLforLocalPath:(NSString *)localPath
 392{
 393    CDVFilesystemURL *localURL = nil;
 394    NSUInteger shortestFullPath = 0;
 395
 396    // Try all installed filesystems, in order. Return the most match url.
 397    for (id object in self.fileSystems) {
 398        if ([object respondsToSelector:@selector(URLforFilesystemPath:)]) {
 399            CDVFilesystemURL *url = [object URLforFilesystemPath:localPath];
 400            if (url){
 401                // A shorter fullPath would imply that the filesystem is a better match for the local path
 402                if (!localURL || ([[url fullPath] length] < shortestFullPath)) {
 403                    localURL = url;
 404                    shortestFullPath = [[url fullPath] length];
 405                }
 406            }
 407        }
 408    }
 409    return localURL;
 410}
 411
 412- (NSNumber*)checkFreeDiskSpace:(NSString*)appPath
 413{
 414    NSFileManager* fMgr = [[NSFileManager alloc] init];
 415
 416    NSError* __autoreleasing pError = nil;
 417
 418    NSDictionary* pDict = [fMgr attributesOfFileSystemForPath:appPath error:&pError];
 419    NSNumber* pNumAvail = (NSNumber*)[pDict objectForKey:NSFileSystemFreeSize];
 420
 421    return pNumAvail;
 422}
 423
 424/* Request the File System info
 425 *
 426 * IN:
 427 * arguments[0] - type (number as string)
 428 *	TEMPORARY = 0, PERSISTENT = 1;
 429 * arguments[1] - size
 430 *
 431 * OUT:
 432 *	Dictionary representing FileSystem object
 433 *		name - the human readable directory name
 434 *		root = DirectoryEntry object
 435 *			bool isDirectory
 436 *			bool isFile
 437 *			string name
 438 *			string fullPath
 439 *			fileSystem = FileSystem object - !! ignored because creates circular reference !!
 440 */
 441
 442- (void)requestFileSystem:(CDVInvokedUrlCommand*)command
 443{
 444    // arguments
 445    NSString* strType = [command argumentAtIndex:0];
 446    unsigned long long size = [[command argumentAtIndex:1] longLongValue];
 447
 448    int type = [strType intValue];
 449    CDVPluginResult* result = nil;
 450
 451    if (type >= self.fileSystems.count) {
 452        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
 453        NSLog(@"No filesystem of type requested");
 454    } else {
 455        NSString* fullPath = @"/";
 456        // check for avail space for size request
 457        NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
 458        // NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]);
 459        if (pNumAvail && ([pNumAvail unsignedLongLongValue] < size)) {
 460            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:QUOTA_EXCEEDED_ERR];
 461        } else {
 462            NSObject<CDVFileSystem> *rootFs = [self.fileSystems objectAtIndex:type];
 463            if (rootFs == nil) {
 464                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
 465                NSLog(@"No filesystem of type requested");
 466            } else {
 467                NSMutableDictionary* fileSystem = [NSMutableDictionary dictionaryWithCapacity:2];
 468                [fileSystem setObject:rootFs.name forKey:@"name"];
 469                NSDictionary* dirEntry = [self makeEntryForPath:fullPath fileSystemName:rootFs.name isDirectory:YES];
 470                [fileSystem setObject:dirEntry forKey:@"root"];
 471                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
 472            }
 473        }
 474    }
 475    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 476}
 477
 478
 479- (void)requestAllFileSystems:(CDVInvokedUrlCommand*)command
 480{
 481    NSMutableArray* ret = [[NSMutableArray alloc] init];
 482    for (NSObject<CDVFileSystem>* root in fileSystems_) {
 483        [ret addObject:[self makeEntryForPath:@"/" fileSystemName:root.name isDirectory:YES]];
 484    }
 485    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:ret];
 486    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 487}
 488
 489- (void)requestAllPaths:(CDVInvokedUrlCommand*)command
 490{
 491    NSString* libPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)[0];
 492    NSString* libPathSync = [libPath stringByAppendingPathComponent:@"Cloud"];
 493    NSString* libPathNoSync = [libPath stringByAppendingPathComponent:@"NoCloud"];
 494    NSString* docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
 495    NSString* storagePath = [libPath stringByDeletingLastPathComponent];
 496    NSString* cachePath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES)[0];
 497
 498    // Create the directories if necessary.
 499    [[NSFileManager defaultManager] createDirectoryAtPath:libPathSync withIntermediateDirectories:YES attributes:nil error:nil];
 500    [[NSFileManager defaultManager] createDirectoryAtPath:libPathNoSync withIntermediateDirectories:YES attributes:nil error:nil];
 501    // Mark NoSync as non-iCloud.
 502    [[NSURL fileURLWithPath:libPathNoSync] setResourceValue: [NSNumber numberWithBool: YES]
 503                                                     forKey: NSURLIsExcludedFromBackupKey error:nil];
 504
 505    NSDictionary* ret = @{
 506        @"applicationDirectory": [[NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]] absoluteString],
 507        @"applicationStorageDirectory": [[NSURL fileURLWithPath:storagePath] absoluteString],
 508        @"dataDirectory": [[NSURL fileURLWithPath:libPathNoSync] absoluteString],
 509        @"syncedDataDirectory": [[NSURL fileURLWithPath:libPathSync] absoluteString],
 510        @"documentsDirectory": [[NSURL fileURLWithPath:docPath] absoluteString],
 511        @"cacheDirectory": [[NSURL fileURLWithPath:cachePath] absoluteString],
 512        @"tempDirectory": [[NSURL fileURLWithPath:NSTemporaryDirectory()] absoluteString]
 513    };
 514
 515    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:ret];
 516    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 517}
 518
 519/* Creates and returns a dictionary representing an Entry Object
 520 *
 521 * IN:
 522 * NSString* fullPath of the entry
 523 * int fsType - FileSystem type
 524 * BOOL isDirectory - YES if this is a directory, NO if is a file
 525 * OUT:
 526 * NSDictionary* Entry object
 527 *		bool as NSNumber isDirectory
 528 *		bool as NSNumber isFile
 529 *		NSString*  name - last part of path
 530 *		NSString* fullPath
 531 *		NSString* filesystemName - FileSystem name -- actual filesystem will be created on the JS side if necessary, to avoid
 532 *         creating circular reference (FileSystem contains DirectoryEntry which contains FileSystem.....!!)
 533 */
 534- (NSDictionary*)makeEntryForPath:(NSString*)fullPath fileSystemName:(NSString *)fsName isDirectory:(BOOL)isDir
 535{
 536    NSObject<CDVFileSystem> *fs = [self fileSystemByName:fsName];
 537    return [fs makeEntryForPath:fullPath isDirectory:isDir];
 538}
 539
 540- (NSDictionary *)makeEntryForLocalURL:(CDVFilesystemURL *)localURL
 541{
 542    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURL];
 543    return [fs makeEntryForLocalURL:localURL];
 544}
 545
 546- (NSDictionary *)makeEntryForURL:(NSURL *)URL
 547{
 548    CDVFilesystemURL* fsURL = [self fileSystemURLforArg:[URL absoluteString]];
 549    return [self makeEntryForLocalURL:fsURL];
 550}
 551
 552/*
 553 * Given a URI determine the File System information associated with it and return an appropriate W3C entry object
 554 * IN
 555 *	NSString* localURI: Should be an escaped local filesystem URI
 556 * OUT
 557 *	Entry object
 558 *		bool isDirectory
 559 *		bool isFile
 560 *		string name
 561 *		string fullPath
 562 *		fileSystem = FileSystem object - !! ignored because creates circular reference FileSystem contains DirectoryEntry which contains FileSystem.....!!
 563 */
 564- (void)resolveLocalFileSystemURI:(CDVInvokedUrlCommand*)command
 565{
 566    // arguments
 567    NSString* localURIstr = [command argumentAtIndex:0];
 568    CDVPluginResult* result;
 569
 570    localURIstr = [self encodePath:localURIstr]; //encode path before resolving
 571    CDVFilesystemURL* inputURI = [self fileSystemURLforArg:localURIstr];
 572
 573    if (inputURI == nil || inputURI.fileSystemName == nil) {
 574        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
 575    } else {
 576        NSObject<CDVFileSystem> *fs = [self filesystemForURL:inputURI];
 577        if (fs == nil) {
 578            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
 579        } else {
 580            result = [fs entryForLocalURI:inputURI];
 581        }
 582    }
 583    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 584}
 585
 586//encode path with percent escapes
 587-(NSString *)encodePath:(NSString *)path
 588{
 589    NSString *decodedPath = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; //decode incase it's already encoded to avoid encoding twice
 590    return [decodedPath stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
 591}
 592
 593
 594/* Part of DirectoryEntry interface,  creates or returns the specified directory
 595 * IN:
 596 *	NSString* localURI - local filesystem URI for this directory
 597 *	NSString* path - directory to be created/returned; may be full path or relative path
 598 *	NSDictionary* - Flags object
 599 *		boolean as NSNumber create -
 600 *			if create is true and directory does not exist, create dir and return directory entry
 601 *			if create is true and exclusive is true and directory does exist, return error
 602 *			if create is false and directory does not exist, return error
 603 *			if create is false and the path represents a file, return error
 604 *		boolean as NSNumber exclusive - used in conjunction with create
 605 *			if exclusive is true and create is true - specifies failure if directory already exists
 606 *
 607 *
 608 */
 609- (void)getDirectory:(CDVInvokedUrlCommand*)command
 610{
 611    NSMutableArray* arguments = [NSMutableArray arrayWithArray:command.arguments];
 612    NSMutableDictionary* options = nil;
 613
 614    if ([arguments count] >= 3) {
 615        options = [command argumentAtIndex:2 withDefault:nil];
 616    }
 617    // add getDir to options and call getFile()
 618    if (options != nil) {
 619        options = [NSMutableDictionary dictionaryWithDictionary:options];
 620    } else {
 621        options = [NSMutableDictionary dictionaryWithCapacity:1];
 622    }
 623    [options setObject:[NSNumber numberWithInt:1] forKey:@"getDir"];
 624    if ([arguments count] >= 3) {
 625        [arguments replaceObjectAtIndex:2 withObject:options];
 626    } else {
 627        [arguments addObject:options];
 628    }
 629    CDVInvokedUrlCommand* subCommand =
 630        [[CDVInvokedUrlCommand alloc] initWithArguments:arguments
 631                                             callbackId:command.callbackId
 632                                              className:command.className
 633                                             methodName:command.methodName];
 634
 635    [self getFile:subCommand];
 636}
 637
 638/* Part of DirectoryEntry interface,  creates or returns the specified file
 639 * IN:
 640 *	NSString* baseURI - local filesytem URI for the base directory to search
 641 *	NSString* requestedPath - file to be created/returned; may be absolute path or relative path
 642 *	NSDictionary* options - Flags object
 643 *		boolean as NSNumber create -
 644 *			if create is true and file does not exist, create file and return File entry
 645 *			if create is true and exclusive is true and file does exist, return error
 646 *			if create is false and file does not exist, return error
 647 *			if create is false and the path represents a directory, return error
 648 *		boolean as NSNumber exclusive - used in conjunction with create
 649 *			if exclusive is true and create is true - specifies failure if file already exists
 650 */
 651- (void)getFile:(CDVInvokedUrlCommand*)command
 652{
 653    NSString* baseURIstr = [command argumentAtIndex:0];
 654    CDVFilesystemURL* baseURI = [self fileSystemURLforArg:baseURIstr];
 655    NSString* requestedPath = [command argumentAtIndex:1];
 656    NSDictionary* options = [command argumentAtIndex:2 withDefault:nil];
 657
 658    NSObject<CDVFileSystem> *fs = [self filesystemForURL:baseURI];
 659    CDVPluginResult* result = [fs getFileForURL:baseURI requestedPath:requestedPath options:options];
 660
 661
 662    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 663}
 664
 665/*
 666 * Look up the parent Entry containing this Entry.
 667 * If this Entry is the root of its filesystem, its parent is itself.
 668 * IN:
 669 * NSArray* arguments
 670 *	0 - NSString* localURI
 671 * NSMutableDictionary* options
 672 *	empty
 673 */
 674- (void)getParent:(CDVInvokedUrlCommand*)command
 675{
 676    // arguments are URL encoded
 677    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 678
 679    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 680    CDVPluginResult* result = [fs getParentForURL:localURI];
 681
 682    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 683}
 684
 685/*
 686 * set MetaData of entry
 687 * Currently we only support "com.apple.MobileBackup" (boolean)
 688 */
 689- (void)setMetadata:(CDVInvokedUrlCommand*)command
 690{
 691    // arguments
 692    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 693    NSDictionary* options = [command argumentAtIndex:1 withDefault:nil];
 694    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 695    CDVPluginResult* result = [fs setMetadataForURL:localURI withObject:options];
 696
 697    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 698}
 699
 700/* removes the directory or file entry
 701 * IN:
 702 * NSArray* arguments
 703 *	0 - NSString* localURI
 704 *
 705 * returns NO_MODIFICATION_ALLOWED_ERR  if is top level directory or no permission to delete dir
 706 * returns INVALID_MODIFICATION_ERR if is non-empty dir or asset library file
 707 * returns NOT_FOUND_ERR if file or dir is not found
 708*/
 709- (void)remove:(CDVInvokedUrlCommand*)command
 710{
 711    // arguments
 712    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 713    CDVPluginResult* result = nil;
 714
 715    if ([localURI.fullPath isEqualToString:@""]) {
 716        // error if try to remove top level (documents or tmp) dir
 717        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
 718    } else {
 719        NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 720        result = [fs removeFileAtURL:localURI];
 721    }
 722    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 723}
 724
 725/* recursively removes the directory
 726 * IN:
 727 * NSArray* arguments
 728 *	0 - NSString* localURI
 729 *
 730 * returns NO_MODIFICATION_ALLOWED_ERR  if is top level directory or no permission to delete dir
 731 * returns NOT_FOUND_ERR if file or dir is not found
 732 */
 733- (void)removeRecursively:(CDVInvokedUrlCommand*)command
 734{
 735    // arguments
 736    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 737    CDVPluginResult* result = nil;
 738
 739    if ([localURI.fullPath isEqualToString:@""]) {
 740        // error if try to remove top level (documents or tmp) dir
 741        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
 742    } else {
 743        NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 744        result = [fs recursiveRemoveFileAtURL:localURI];
 745    }
 746    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 747}
 748
 749- (void)copyTo:(CDVInvokedUrlCommand*)command
 750{
 751    [self doCopyMove:command isCopy:YES];
 752}
 753
 754- (void)moveTo:(CDVInvokedUrlCommand*)command
 755{
 756    [self doCopyMove:command isCopy:NO];
 757}
 758
 759/* Copy/move a file or directory to a new location
 760 * IN:
 761 * NSArray* arguments
 762 *	0 - NSString* URL of entry to copy
 763 *  1 - NSString* URL of the directory into which to copy/move the entry
 764 *  2 - Optionally, the new name of the entry, defaults to the current name
 765 *	BOOL - bCopy YES if copy, NO if move
 766 */
 767- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy
 768{
 769    NSArray* arguments = command.arguments;
 770
 771    // arguments
 772    NSString* srcURLstr = [command argumentAtIndex:0];
 773    NSString* destURLstr = [command argumentAtIndex:1];
 774
 775    CDVPluginResult *result;
 776
 777    if (!srcURLstr || !destURLstr) {
 778        // either no source or no destination provided
 779        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
 780        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 781        return;
 782    }
 783
 784    CDVFilesystemURL* srcURL = [self fileSystemURLforArg:srcURLstr];
 785    CDVFilesystemURL* destURL = [self fileSystemURLforArg:destURLstr];
 786
 787    NSObject<CDVFileSystem> *srcFs = [self filesystemForURL:srcURL];
 788    NSObject<CDVFileSystem> *destFs = [self filesystemForURL:destURL];
 789
 790    // optional argument; use last component from srcFullPath if new name not provided
 791    NSString* newName = ([arguments count] > 2) ? [command argumentAtIndex:2] : [srcURL.url lastPathComponent];
 792    if ([newName rangeOfString:@":"].location != NSNotFound) {
 793        // invalid chars in new name
 794        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
 795        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 796        return;
 797    }
 798
 799    __weak CDVFile* weakSelf = self;
 800    [self.commandDelegate runInBackground:^ {
 801        [destFs copyFileToURL:destURL withName:newName fromFileSystem:srcFs atURL:srcURL copy:bCopy callback:^(CDVPluginResult* result) {
 802            [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 803        }];
 804    }];
 805
 806}
 807
 808- (void)getFileMetadata:(CDVInvokedUrlCommand*)command
 809{
 810    // arguments
 811    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 812    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 813    __weak CDVFile* weakSelf = self;
 814    [fs getFileMetadataForURL:localURI callback:^(CDVPluginResult* result) {
 815        [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 816    }];
 817}
 818
 819- (void)readEntries:(CDVInvokedUrlCommand*)command
 820{
 821    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 822    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 823    CDVPluginResult *result = [fs readEntriesAtURL:localURI];
 824
 825    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 826}
 827
 828/* read and return file data
 829 * IN:
 830 * NSArray* arguments
 831 *	0 - NSString* fullPath
 832 *	1 - NSString* encoding
 833 *	2 - NSString* start
 834 *	3 - NSString* end
 835 */
 836- (void)readAsText:(CDVInvokedUrlCommand*)command
 837{
 838    // arguments
 839    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 840    NSString* encoding = [command argumentAtIndex:1];
 841    NSInteger start = [[command argumentAtIndex:2] integerValue];
 842    NSInteger end = [[command argumentAtIndex:3] integerValue];
 843
 844    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 845
 846    if (fs == nil) {
 847        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
 848        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 849        return;
 850    }
 851
 852    // TODO: implement
 853    if ([@"UTF-8" caseInsensitiveCompare : encoding] != NSOrderedSame) {
 854        NSLog(@"Only UTF-8 encodings are currently supported by readAsText");
 855        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:ENCODING_ERR];
 856        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 857        return;
 858    }
 859
 860    __weak CDVFile* weakSelf = self;
 861
 862    [self.commandDelegate runInBackground:^ {
 863        [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
 864            CDVPluginResult* result = nil;
 865            if (data != nil) {
 866                NSString* str = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSUTF8StringEncoding freeWhenDone:NO];
 867                // Check that UTF8 conversion did not fail.
 868                if (str != nil) {
 869                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:str];
 870                    result.associatedObject = data;
 871                } else {
 872                    errorCode = ENCODING_ERR;
 873                }
 874            }
 875            if (result == nil) {
 876                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
 877            }
 878
 879            [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 880        }];
 881    }];
 882}
 883
 884/* Read content of text file and return as base64 encoded data url.
 885 * IN:
 886 * NSArray* arguments
 887 *	0 - NSString* fullPath
 888 *	1 - NSString* start
 889 *	2 - NSString* end
 890 *
 891 * Determines the mime type from the file extension, returns ENCODING_ERR if mimetype can not be determined.
 892 */
 893
 894- (void)readAsDataURL:(CDVInvokedUrlCommand*)command
 895{
 896    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 897    NSInteger start = [[command argumentAtIndex:1] integerValue];
 898    NSInteger end = [[command argumentAtIndex:2] integerValue];
 899
 900    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 901
 902    __weak CDVFile* weakSelf = self;
 903    [self.commandDelegate runInBackground:^ {
 904        [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
 905            CDVPluginResult* result = nil;
 906            if (data != nil) {
 907                NSString* b64Str = toBase64(data);
 908                NSString* output = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, b64Str];
 909                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:output];
 910            } else {
 911                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
 912            }
 913
 914            [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 915        }];
 916    }];
 917}
 918
 919/* Read content of text file and return as an arraybuffer
 920 * IN:
 921 * NSArray* arguments
 922 *	0 - NSString* fullPath
 923 *	1 - NSString* start
 924 *	2 - NSString* end
 925 */
 926
 927- (void)readAsArrayBuffer:(CDVInvokedUrlCommand*)command
 928{
 929    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 930    NSInteger start = [[command argumentAtIndex:1] integerValue];
 931    NSInteger end = [[command argumentAtIndex:2] integerValue];
 932
 933    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 934
 935    __weak CDVFile* weakSelf = self;
 936
 937    [self.commandDelegate runInBackground:^ {
 938        [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
 939            CDVPluginResult* result = nil;
 940            if (data != nil) {
 941                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArrayBuffer:data];
 942            } else {
 943                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
 944            }
 945
 946            [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 947        }];
 948    }];
 949}
 950
 951- (void)readAsBinaryString:(CDVInvokedUrlCommand*)command
 952{
 953    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 954    NSInteger start = [[command argumentAtIndex:1] integerValue];
 955    NSInteger end = [[command argumentAtIndex:2] integerValue];
 956
 957    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 958
 959    __weak CDVFile* weakSelf = self;
 960
 961    [self.commandDelegate runInBackground:^ {
 962        [fs readFileAtURL:localURI start:start end:end callback:^(NSData* data, NSString* mimeType, CDVFileError errorCode) {
 963            CDVPluginResult* result = nil;
 964            if (data != nil) {
 965                NSString* payload = [[NSString alloc] initWithBytesNoCopy:(void*)[data bytes] length:[data length] encoding:NSASCIIStringEncoding freeWhenDone:NO];
 966                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:payload];
 967                result.associatedObject = data;
 968            } else {
 969                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
 970            }
 971
 972            [weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 973        }];
 974    }];
 975}
 976
 977
 978- (void)truncate:(CDVInvokedUrlCommand*)command
 979{
 980    // arguments
 981    CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
 982    unsigned long long pos = (unsigned long long)[[command argumentAtIndex:1] longLongValue];
 983
 984    NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
 985    CDVPluginResult *result = [fs truncateFileAtURL:localURI atPosition:pos];
 986
 987    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
 988}
 989
 990/* write
 991 * IN:
 992 * NSArray* arguments
 993 *  0 - NSString* localURI of file to write to
 994 *  1 - NSString* or NSData* data to write
 995 *  2 - NSNumber* position to begin writing
 996 */
 997- (void)write:(CDVInvokedUrlCommand*)command
 998{
 999    __weak CDVFile* weakSelf = self;
1000
1001    [self.commandDelegate runInBackground:^ {
1002        NSString* callbackId = command.callbackId;
1003
1004        // arguments
1005        CDVFilesystemURL* localURI = [self fileSystemURLforArg:command.arguments[0]];
1006        id argData = [command argumentAtIndex:1];
1007        unsigned long long pos = (unsigned long long)[[command argumentAtIndex:2] longLongValue];
1008
1009        NSObject<CDVFileSystem> *fs = [self filesystemForURL:localURI];
1010
1011
1012        [fs truncateFileAtURL:localURI atPosition:pos];
1013        CDVPluginResult *result;
1014        if ([argData isKindOfClass:[NSString class]]) {
1015            NSData *encData = [argData dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
1016            result = [fs writeToFileAtURL:localURI withData:encData append:YES];
1017        } else if ([argData isKindOfClass:[NSData class]]) {
1018            result = [fs writeToFileAtURL:localURI withData:argData append:YES];
1019        } else {
1020            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Invalid parameter type"];
1021        }
1022        [weakSelf.commandDelegate sendPluginResult:result callbackId:callbackId];
1023    }];
1024}
1025
1026#pragma mark Methods for converting between URLs and paths
1027
1028- (NSString *)filesystemPathForURL:(CDVFilesystemURL *)localURL
1029{
1030    for (NSObject<CDVFileSystem> *fs in self.fileSystems) {
1031        if ([fs.name isEqualToString:localURL.fileSystemName]) {
1032            if ([fs respondsToSelector:@selector(filesystemPathForURL:)]) {
1033                return [fs filesystemPathForURL:localURL];
1034            }
1035        }
1036    }
1037    return nil;
1038}
1039
1040#pragma mark Undocumented Filesystem API
1041
1042- (void)testFileExists:(CDVInvokedUrlCommand*)command
1043{
1044    // arguments
1045    NSString* argPath = [command argumentAtIndex:0];
1046
1047    // Get the file manager
1048    NSFileManager* fMgr = [NSFileManager defaultManager];
1049    NSString* appFile = argPath; // [ self getFullPath: argPath];
1050
1051    BOOL bExists = [fMgr fileExistsAtPath:appFile];
1052    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:(bExists ? 1 : 0)];
1053
1054    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
1055}
1056
1057- (void)testDirectoryExists:(CDVInvokedUrlCommand*)command
1058{
1059    // arguments
1060    NSString* argPath = [command argumentAtIndex:0];
1061
1062    // Get the file manager
1063    NSFileManager* fMgr = [[NSFileManager alloc] init];
1064    NSString* appFile = argPath; // [self getFullPath: argPath];
1065    BOOL bIsDir = NO;
1066    BOOL bExists = [fMgr fileExistsAtPath:appFile isDirectory:&bIsDir];
1067
1068    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:((bExists && bIsDir) ? 1 : 0)];
1069
1070    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
1071}
1072
1073// Returns number of bytes available via callback
1074- (void)getFreeDiskSpace:(CDVInvokedUrlCommand*)command
1075{
1076    // no arguments
1077    
1078    NSNumber* pNumAvail = [self checkFreeDiskSpace:self.rootDocsPath];
1079
1080    NSString* strFreeSpace = [NSString stringWithFormat:@"%qu", [pNumAvail unsignedLongLongValue]];
1081    // NSLog(@"Free space is %@", strFreeSpace );
1082
1083    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:strFreeSpace];
1084
1085    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
1086}
1087
1088#pragma mark Compatibility with older File API
1089
1090- (NSString*)getMimeTypeFromPath:(NSString*)fullPath
1091{
1092    return [CDVLocalFilesystem getMimeTypeFromPath:fullPath];
1093}
1094
1095- (NSDictionary *)getDirectoryEntry:(NSString *)localPath isDirectory:(BOOL)bDirRequest
1096{
1097    CDVFilesystemURL *localURL = [self fileSystemURLforLocalPath:localPath];
1098    return [self makeEntryForPath:localURL.fullPath fileSystemName:localURL.fileSystemName isDirectory:bDirRequest];
1099}
1100
1101#pragma mark Internal methods for testing
1102// Internal methods for testing: Get the on-disk location of a local filesystem url.
1103// [Currently used for testing file-transfer]
1104
1105- (void)_getLocalFilesystemPath:(CDVInvokedUrlCommand*)command
1106{
1107    CDVFilesystemURL* localURL = [self fileSystemURLforArg:command.arguments[0]];
1108
1109    NSString* fsPath = [self filesystemPathForURL:localURL];
1110    CDVPluginResult* result;
1111    if (fsPath) {
1112        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:fsPath];
1113    } else {
1114        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Cannot resolve URL to a file"];
1115    }
1116    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
1117}
1118
1119@end