cordova-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fil...@apache.org
Subject [12/52] [partial] support for 2.4.0rc1. "vendored" the platform libs in. added Gord and Braden as contributors. removed dependency on unzip and axed the old download-cordova code.
Date Tue, 22 Jan 2013 01:57:59 GMT
http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVFile.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVFile.m b/lib/cordova-ios/CordovaLib/Classes/CDVFile.m
new file mode 100644
index 0000000..11b2b77
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVFile.m
@@ -0,0 +1,1279 @@
+/*
+ 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 "NSArray+Comparisons.h"
+#import "NSDictionary+Extensions.h"
+#import "CDVJSON.h"
+#import "NSData+Base64.h"
+#import <MobileCoreServices/MobileCoreServices.h>
+#import "CDVAvailability.h"
+#import "sys/xattr.h"
+
+extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));
+
+#ifndef __IPHONE_5_1
+    NSString* const NSURLIsExcludedFromBackupKey = @"NSURLIsExcludedFromBackupKey";
+#endif
+
+NSString* const kCDVAssetsLibraryPrefix = @"assets-library://";
+
+@implementation CDVFile
+
+@synthesize appDocsPath, appLibraryPath, appTempPath, persistentPath, temporaryPath, userHasAllowed;
+
+- (id)initWithWebView:(UIWebView*)theWebView
+{
+    self = (CDVFile*)[super initWithWebView:theWebView];
+    if (self) {
+        // get the documents directory path
+        NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+        self.appDocsPath = [paths objectAtIndex:0];
+
+        paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
+        self.appLibraryPath = [paths objectAtIndex:0];
+
+        self.appTempPath = [NSTemporaryDirectory ()stringByStandardizingPath];  // remove trailing slash from NSTemporaryDirectory()
+
+        self.persistentPath = [NSString stringWithFormat:@"/%@", [self.appDocsPath lastPathComponent]];
+        self.temporaryPath = [NSString stringWithFormat:@"/%@", [self.appTempPath lastPathComponent]];
+        // NSLog(@"docs: %@ - temp: %@", self.appDocsPath, self.appTempPath);
+    }
+
+    return self;
+}
+
+- (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;
+}
+
+// figure out if the pathFragment represents a persistent of temporary directory and return the full application path.
+// returns nil if path is not persistent or temporary
+- (NSString*)getAppPath:(NSString*)pathFragment
+{
+    NSString* appPath = nil;
+    NSRange rangeP = [pathFragment rangeOfString:self.persistentPath];
+    NSRange rangeT = [pathFragment rangeOfString:self.temporaryPath];
+
+    if ((rangeP.location != NSNotFound) && (rangeT.location != NSNotFound)) {
+        // we found both in the path, return whichever one is first
+        if (rangeP.length < rangeT.length) {
+            appPath = self.appDocsPath;
+        } else {
+            appPath = self.appTempPath;
+        }
+    } else if (rangeP.location != NSNotFound) {
+        appPath = self.appDocsPath;
+    } else if (rangeT.location != NSNotFound) {
+        appPath = self.appTempPath;
+    }
+    return appPath;
+}
+
+/* get the full path to this resource
+ * IN
+ *	NSString* pathFragment - full Path from File or Entry object (includes system path info)
+ * OUT
+ *	NSString* fullPath - full iOS path to this resource,  nil if not found
+ */
+
+/*  Was here in order to NOT have to return full path, but W3C synchronous DirectoryEntry.toURI() killed that idea since I can't call into iOS to
+ * resolve full URI.  Leaving this code here in case W3C spec changes.
+-(NSString*) getFullPath: (NSString*)pathFragment
+{
+    return pathFragment;
+    NSString* fullPath = nil;
+    NSString *appPath = [ self getAppPath: pathFragment];
+    if (appPath){
+
+        // remove last component from appPath
+        NSRange range = [appPath rangeOfString:@"/" options: NSBackwardsSearch];
+        NSString* newPath = [appPath substringToIndex:range.location];
+        // add pathFragment to get test Path
+        fullPath = [newPath stringByAppendingPathComponent:pathFragment];
+    }
+    return fullPath;
+} */
+
+/* Request the File System info
+ *
+ * IN:
+ * 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
+{
+    NSArray* arguments = command.arguments;
+
+    // arguments
+    NSString* strType = [arguments objectAtIndex:0];
+    unsigned long long size = [[arguments objectAtIndex:1] longLongValue];
+
+    int type = [strType intValue];
+    CDVPluginResult* result = nil;
+
+    if (type > 1) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:NOT_FOUND_ERR];
+        NSLog(@"iOS only supports TEMPORARY and PERSISTENT file systems");
+    } else {
+        // NSString* fullPath = [NSString stringWithFormat:@"/%@", (type == 0 ? [self.appTempPath lastPathComponent] : [self.appDocsPath lastPathComponent])];
+        NSString* fullPath = (type == 0 ? self.appTempPath  : self.appDocsPath);
+        // check for avail space for size request
+        NSNumber* pNumAvail = [self checkFreeDiskSpace:fullPath];
+        // NSLog(@"Free space: %@", [NSString stringWithFormat:@"%qu", [ pNumAvail unsignedLongLongValue ]]);
+        if (pNumAvail && ([pNumAvail unsignedLongLongValue] < size)) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:QUOTA_EXCEEDED_ERR];
+        } else {
+            NSMutableDictionary* fileSystem = [NSMutableDictionary dictionaryWithCapacity:2];
+            [fileSystem setObject:(type == TEMPORARY ? kW3FileTemporary:kW3FilePersistent) forKey:@"name"];
+            NSDictionary* dirEntry = [self getDirectoryEntry:fullPath isDirectory:YES];
+            [fileSystem setObject:dirEntry forKey:@"root"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+        }
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* Creates a dictionary representing an Entry Object
+ *
+ * IN:
+ * NSString* fullPath of the entry
+ * 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
+ *		fileSystem = FileSystem object - !! ignored because creates circular reference FileSystem contains DirectoryEntry which contains FileSystem.....!!
+ */
+- (NSDictionary*)getDirectoryEntry:(NSString*)fullPath isDirectory:(BOOL)isDir
+{
+    NSMutableDictionary* dirEntry = [NSMutableDictionary dictionaryWithCapacity:4];
+    NSString* lastPart = [fullPath lastPathComponent];
+
+    [dirEntry setObject:[NSNumber numberWithBool:!isDir]  forKey:@"isFile"];
+    [dirEntry setObject:[NSNumber numberWithBool:isDir]  forKey:@"isDirectory"];
+    // NSURL* fileUrl = [NSURL fileURLWithPath:fullPath];
+    // [dirEntry setObject: [fileUrl absoluteString] forKey: @"fullPath"];
+    [dirEntry setObject:fullPath forKey:@"fullPath"];
+    [dirEntry setObject:lastPart forKey:@"name"];
+
+    return dirEntry;
+}
+
+/*
+ * Given a URI determine the File System information associated with it and return an appropriate W3C entry object
+ * IN
+ *	NSString* fileURI  - currently requires full file URI
+ * 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* inputUri = [command.arguments objectAtIndex:0];
+
+    // don't know if string is encoded or not so unescape
+    NSString* cleanUri = [inputUri stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+    // now escape in order to create URL
+    NSString* strUri = [cleanUri stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+    NSURL* testUri = [NSURL URLWithString:strUri];
+    CDVPluginResult* result = nil;
+
+    if (!testUri || ![testUri isFileURL]) {
+        // issue ENCODING_ERR
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:ENCODING_ERR];
+    } else {
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        NSString* path = [testUri path];
+        // NSLog(@"url path: %@", path);
+        BOOL isDir = NO;
+        // see if exists and is file or dir
+        BOOL bExists = [fileMgr fileExistsAtPath:path isDirectory:&isDir];
+        if (bExists) {
+            // see if it contains docs path
+            NSRange range = [path rangeOfString:self.appDocsPath];
+            NSString* foundFullPath = nil;
+            // there's probably an api or easier way to figure out the path type but I can't find it!
+            if ((range.location != NSNotFound) && (range.length == [self.appDocsPath length])) {
+                foundFullPath = self.appDocsPath;
+            } else {
+                // see if it contains the temp path
+                range = [path rangeOfString:self.appTempPath];
+                if ((range.location != NSNotFound) && (range.length == [self.appTempPath length])) {
+                    foundFullPath = self.appTempPath;
+                }
+            }
+            if (foundFullPath == nil) {
+                // error SECURITY_ERR - not one of the two paths types supported
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SECURITY_ERR];
+            } else {
+                NSDictionary* fileSystem = [self getDirectoryEntry:path isDirectory:isDir];
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileSystem];
+            }
+        } else {
+            // return NOT_FOUND_ERR
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+        }
+    }
+    if (result != nil) {
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    }
+}
+
+/* Part of DirectoryEntry interface,  creates or returns the specified directory
+ * IN:
+ *	NSString* fullPath - full path 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 = [arguments objectAtIndex: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* fullPath - full path for this file
+ *	NSString* path - file to be created/returned; may be full path or relative path
+ *	NSDictionary* - 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
+{
+    // arguments are URL encoded
+    NSString* fullPath = [command.arguments objectAtIndex:0];
+    NSString* requestedPath = [command.arguments objectAtIndex:1];
+    NSDictionary* options = [command.arguments objectAtIndex:2 withDefault:nil];
+
+    // return unsupported result for assets-library URLs
+    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFile not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+    BOOL bDirRequest = NO;
+    BOOL create = NO;
+    BOOL exclusive = NO;
+    int errorCode = 0;  // !!! risky - no error code currently defined for 0
+
+    if ([options valueForKeyIsNumber:@"create"]) {
+        create = [(NSNumber*)[options valueForKey:@"create"] boolValue];
+    }
+    if ([options valueForKeyIsNumber:@"exclusive"]) {
+        exclusive = [(NSNumber*)[options valueForKey:@"exclusive"] boolValue];
+    }
+
+    if ([options valueForKeyIsNumber:@"getDir"]) {
+        // this will not exist for calls directly to getFile but will have been set by getDirectory before calling this method
+        bDirRequest = [(NSNumber*)[options valueForKey:@"getDir"] boolValue];
+    }
+    // see if the requested path has invalid characters - should we be checking for  more than just ":"?
+    if ([requestedPath rangeOfString:@":"].location != NSNotFound) {
+        errorCode = ENCODING_ERR;
+    } else {
+        // was full or relative path provided?
+        NSRange range = [requestedPath rangeOfString:fullPath];
+        BOOL bIsFullPath = range.location != NSNotFound;
+
+        NSString* reqFullPath = nil;
+
+        if (!bIsFullPath) {
+            reqFullPath = [fullPath stringByAppendingPathComponent:requestedPath];
+        } else {
+            reqFullPath = requestedPath;
+        }
+
+        // NSLog(@"reqFullPath = %@", reqFullPath);
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir;
+        BOOL bExists = [fileMgr fileExistsAtPath:reqFullPath isDirectory:&bIsDir];
+        if (bExists && (create == NO) && (bIsDir == !bDirRequest)) {
+            // path exists and is of requested type  - return TYPE_MISMATCH_ERR
+            errorCode = TYPE_MISMATCH_ERR;
+        } else if (!bExists && (create == NO)) {
+            // path does not exist and create is false - return NOT_FOUND_ERR
+            errorCode = NOT_FOUND_ERR;
+        } else if (bExists && (create == YES) && (exclusive == YES)) {
+            // file/dir already exists and exclusive and create are both true - return PATH_EXISTS_ERR
+            errorCode = PATH_EXISTS_ERR;
+        } else {
+            // if bExists and create == YES - just return data
+            // if bExists and create == NO  - just return data
+            // if !bExists and create == YES - create and return data
+            BOOL bSuccess = YES;
+            NSError __autoreleasing* pError = nil;
+            if (!bExists && (create == YES)) {
+                if (bDirRequest) {
+                    // create the dir
+                    bSuccess = [fileMgr createDirectoryAtPath:reqFullPath withIntermediateDirectories:NO attributes:nil error:&pError];
+                } else {
+                    // create the empty file
+                    bSuccess = [fileMgr createFileAtPath:reqFullPath 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 getDirectoryEntry:reqFullPath isDirectory:bDirRequest]];
+            }
+        } // are all possible conditions met?
+    }
+
+    if (errorCode > 0) {
+        // create error callback
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+    }
+
+    [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* fullPath
+ * NSMutableDictionary* options
+ *	empty
+ */
+- (void)getParent:(CDVInvokedUrlCommand*)command
+{
+    // arguments are URL encoded
+    NSString* fullPath = [command.arguments objectAtIndex:0];
+
+    // return unsupported result for assets-library URLs
+    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"remove not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+    NSString* newPath = nil;
+
+    if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+        // return self
+        newPath = fullPath;
+    } else {
+        // since this call is made from an existing Entry object - the parent should already exist so no additional error checking
+        // remove last component and return Entry
+        NSRange range = [fullPath rangeOfString:@"/" options:NSBackwardsSearch];
+        newPath = [fullPath substringToIndex:range.location];
+    }
+
+    if (newPath) {
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir;
+        BOOL bExists = [fileMgr fileExistsAtPath:newPath isDirectory:&bIsDir];
+        if (bExists) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getDirectoryEntry:newPath isDirectory:bIsDir]];
+        }
+    }
+    if (!result) {
+        // invalid path or file does not exist
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * get MetaData of entry
+ * Currently MetaData only includes modificationTime.
+ */
+- (void)getMetadata:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command.arguments objectAtIndex:0];
+
+    // return unsupported result for assets-library URLs
+    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getMetadata not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    NSString* testPath = argPath; // [self getFullPath: argPath];
+
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    NSError* __autoreleasing error = nil;
+    CDVPluginResult* result = nil;
+
+    NSDictionary* fileAttribs = [fileMgr attributesOfItemAtPath:testPath error:&error];
+
+    if (fileAttribs) {
+        NSDate* modDate = [fileAttribs fileModificationDate];
+        if (modDate) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:[modDate timeIntervalSince1970] * 1000];
+        }
+    } else {
+        // didn't get fileAttribs
+        CDVFileError errorCode = ABORT_ERR;
+        NSLog(@"error getting metadata: %@", [error localizedDescription]);
+        if ([error code] == NSFileNoSuchFileError) {
+            errorCode = NOT_FOUND_ERR;
+        }
+        // log [NSNumber numberWithDouble: theMessage] objCtype to see what it returns
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errorCode];
+    }
+    if (!result) {
+        // invalid path or file does not exist
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/*
+ * set MetaData of entry
+ * Currently we only support "com.apple.MobileBackup" (boolean)
+ */
+- (void)setMetadata:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* filePath = [command.arguments objectAtIndex:0];
+    NSDictionary* options = [command.arguments objectAtIndex:1 withDefault:nil];
+
+    // return unsupported result for assets-library URLs
+    if ([filePath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"setMetadata not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+    BOOL ok = NO;
+
+    // we only care about this iCloud key for now.
+    // set to 1/true to skip backup, set to 0/false to back it up (effectively removing the attribute)
+    NSString* iCloudBackupExtendedAttributeKey = @"com.apple.MobileBackup";
+    id iCloudBackupExtendedAttributeValue = [options objectForKey:iCloudBackupExtendedAttributeKey];
+
+    if ((iCloudBackupExtendedAttributeValue != nil) && [iCloudBackupExtendedAttributeValue isKindOfClass:[NSNumber class]]) {
+        if (IsAtLeastiOSVersion(@"5.1")) {
+            NSURL* url = [NSURL fileURLWithPath:filePath];
+            NSError* __autoreleasing error = nil;
+
+            ok = [url setResourceValue:[NSNumber numberWithBool:[iCloudBackupExtendedAttributeValue boolValue]] forKey:NSURLIsExcludedFromBackupKey error:&error];
+        } else { // below 5.1 (deprecated - only really supported in 5.01)
+            u_int8_t value = [iCloudBackupExtendedAttributeValue intValue];
+            if (value == 0) { // remove the attribute (allow backup, the default)
+                ok = (removexattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], 0) == 0);
+            } else { // set the attribute (skip backup)
+                ok = (setxattr([filePath fileSystemRepresentation], [iCloudBackupExtendedAttributeKey cStringUsingEncoding:NSUTF8StringEncoding], &value, sizeof(value), 0, 0) == 0);
+            }
+        }
+    }
+
+    if (ok) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+    } else {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* removes the directory or file entry
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath
+ *
+ * returns NO_MODIFICATION_ALLOWED_ERR  if is top level directory or no permission to delete dir
+ * returns INVALID_MODIFICATION_ERR if is dir and is not empty
+ * returns NOT_FOUND_ERR if file or dir is not found
+*/
+- (void)remove:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* fullPath = [command.arguments objectAtIndex:0];
+
+    // return unsupported result for assets-library URLs
+    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"remove not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+    CDVFileError errorCode = 0;  // !! 0 not currently defined
+
+    // error if try to remove top level (documents or tmp) dir
+    if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+        errorCode = NO_MODIFICATION_ALLOWED_ERR;
+    } else {
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir = NO;
+        BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir];
+        if (!bExists) {
+            errorCode = NOT_FOUND_ERR;
+        }
+        if (bIsDir && ([[fileMgr contentsOfDirectoryAtPath:fullPath error:nil] count] != 0)) {
+            // dir is not empty
+            errorCode = INVALID_MODIFICATION_ERR;
+        }
+    }
+    if (errorCode > 0) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+    } else {
+        // perform actual remove
+        result = [self doRemove:fullPath];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* recursively removes the directory
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath
+ *
+ * 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
+    NSString* fullPath = [command.arguments objectAtIndex:0];
+
+    // return unsupported result for assets-library URLs
+    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"removeRecursively not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+
+    // error if try to remove top level (documents or tmp) dir
+    if ([fullPath isEqualToString:self.appDocsPath] || [fullPath isEqualToString:self.appTempPath]) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NO_MODIFICATION_ALLOWED_ERR];
+    } else {
+        result = [self doRemove:fullPath];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* remove the file or directory (recursively)
+ * IN:
+ * NSString* fullPath - the full path to the file or directory to be removed
+ * NSString* callbackId
+ * called from remove and removeRecursively - check all pubic api specific error conditions (dir not empty, etc) before calling
+ */
+
+- (CDVPluginResult*)doRemove:(NSString*)fullPath
+{
+    CDVPluginResult* result = nil;
+    BOOL bSuccess = NO;
+    NSError* __autoreleasing pError = nil;
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+    @try {
+        bSuccess = [fileMgr removeItemAtPath:fullPath error:&pError];
+        if (bSuccess) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+        } else {
+            // see if we can give a useful error
+            CDVFileError errorCode = ABORT_ERR;
+            NSLog(@"error getting metadata: %@", [pError localizedDescription]);
+            if ([pError code] == NSFileNoSuchFileError) {
+                errorCode = NOT_FOUND_ERR;
+            } else if ([pError code] == NSFileWriteNoPermissionError) {
+                errorCode = NO_MODIFICATION_ALLOWED_ERR;
+            }
+
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errorCode];
+        }
+    } @catch(NSException* e) {  // NSInvalidArgumentException if path is . or ..
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:SYNTAX_ERR];
+    }
+
+    return result;
+}
+
+- (void)copyTo:(CDVInvokedUrlCommand*)command
+{
+    [self doCopyMove:command isCopy:YES];
+}
+
+- (void)moveTo:(CDVInvokedUrlCommand*)command
+{
+    [self doCopyMove:command isCopy:NO];
+}
+
+/**
+ * Helper function to check to see if the user attempted to copy an entry into its parent without changing its name,
+ * or attempted to copy a directory into a directory that it contains directly or indirectly.
+ *
+ * IN:
+ *  NSString* srcDir
+ *  NSString* destinationDir
+ * OUT:
+ *  YES copy/ move is allows
+ *  NO move is onto itself
+ */
+- (BOOL)canCopyMoveSrc:(NSString*)src ToDestination:(NSString*)dest
+{
+    // This weird test is to determine if we are copying or moving a directory into itself.
+    // Copy /Documents/myDir to /Documents/myDir-backup is okay but
+    // Copy /Documents/myDir to /Documents/myDir/backup not okay
+    BOOL copyOK = YES;
+    NSRange range = [dest rangeOfString:src];
+
+    if (range.location != NSNotFound) {
+        NSRange testRange = {range.length - 1, ([dest length] - range.length)};
+        NSRange resultRange = [dest rangeOfString:@"/" options:0 range:testRange];
+        if (resultRange.location != NSNotFound) {
+            copyOK = NO;
+        }
+    }
+    return copyOK;
+}
+
+/* Copy/move a file or directory to a new location
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath of entry
+ *  1 - NSString* newName the new name of the entry, defaults to the current name
+ *	NSMutableDictionary* options - DirectoryEntry to which to copy the entry
+ *	BOOL - bCopy YES if copy, NO if move
+ *
+ */
+- (void)doCopyMove:(CDVInvokedUrlCommand*)command isCopy:(BOOL)bCopy
+{
+    NSArray* arguments = command.arguments;
+
+    // arguments
+    NSString* srcFullPath = [arguments objectAtIndex:0];
+    NSString* destRootPath = [arguments objectAtIndex:1];
+    // optional argument
+    NSString* newName = ([arguments count] > 2) ? [arguments objectAtIndex:2] : [srcFullPath lastPathComponent];          // use last component from appPath if new name not provided
+
+    // return unsupported result for assets-library URLs
+    if ([srcFullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"moveTo/copyTo not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+    CDVFileError errCode = 0;  // !! Currently 0 is not defined, use this to signal error !!
+
+    /*NSString* destRootPath = nil;
+    NSString* key = @"fullPath";
+    if([options valueForKeyIsString:key]){
+       destRootPath = [options objectForKey:@"fullPath"];
+    }*/
+
+    if (!destRootPath) {
+        // no destination provided
+        errCode = NOT_FOUND_ERR;
+    } else if ([newName rangeOfString:@":"].location != NSNotFound) {
+        // invalid chars in new name
+        errCode = ENCODING_ERR;
+    } else {
+        NSString* newFullPath = [destRootPath stringByAppendingPathComponent:newName];
+        if ([newFullPath isEqualToString:srcFullPath]) {
+            // source and destination can not be the same
+            errCode = INVALID_MODIFICATION_ERR;
+        } else {
+            NSFileManager* fileMgr = [[NSFileManager alloc] init];
+
+            BOOL bSrcIsDir = NO;
+            BOOL bDestIsDir = NO;
+            BOOL bNewIsDir = NO;
+            BOOL bSrcExists = [fileMgr fileExistsAtPath:srcFullPath isDirectory:&bSrcIsDir];
+            BOOL bDestExists = [fileMgr fileExistsAtPath:destRootPath isDirectory:&bDestIsDir];
+            BOOL bNewExists = [fileMgr fileExistsAtPath:newFullPath isDirectory:&bNewIsDir];
+            if (!bSrcExists || !bDestExists) {
+                // the source or the destination root does not exist
+                errCode = NOT_FOUND_ERR;
+            } else if (bSrcIsDir && (bNewExists && !bNewIsDir)) {
+                // can't copy/move dir to file
+                errCode = INVALID_MODIFICATION_ERR;
+            } else { // no errors yet
+                NSError* __autoreleasing error = nil;
+                BOOL bSuccess = NO;
+                if (bCopy) {
+                    if (bSrcIsDir && ![self canCopyMoveSrc:srcFullPath ToDestination:newFullPath] /*[newFullPath hasPrefix:srcFullPath]*/) {
+                        // can't copy dir into self
+                        errCode = INVALID_MODIFICATION_ERR;
+                    } else if (bNewExists) {
+                        // the full destination should NOT already exist if a copy
+                        errCode = PATH_EXISTS_ERR;
+                    } else {
+                        bSuccess = [fileMgr copyItemAtPath:srcFullPath toPath:newFullPath error:&error];
+                    }
+                } 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:newFullPath]) {    // [newFullPath hasPrefix:srcFullPath]){
+                        // can't move a dir into itself
+                        errCode = INVALID_MODIFICATION_ERR;
+                    } else if (bNewExists) {
+                        if (bNewIsDir && ([[fileMgr contentsOfDirectoryAtPath:newFullPath error:NULL] count] != 0)) {
+                            // can't move dir to a dir that is not empty
+                            errCode = INVALID_MODIFICATION_ERR;
+                            newFullPath = nil;  // so we won't try to move
+                        } else {
+                            // remove destination so can perform the moveItemAtPath
+                            bSuccess = [fileMgr removeItemAtPath:newFullPath error:NULL];
+                            if (!bSuccess) {
+                                errCode = INVALID_MODIFICATION_ERR; // is this the correct error?
+                                newFullPath = nil;
+                            }
+                        }
+                    } else if (bNewIsDir && [newFullPath hasPrefix:srcFullPath]) {
+                        // can't move a directory inside itself or to any child at any depth;
+                        errCode = INVALID_MODIFICATION_ERR;
+                        newFullPath = nil;
+                    }
+
+                    if (newFullPath != nil) {
+                        bSuccess = [fileMgr moveItemAtPath:srcFullPath toPath:newFullPath error:&error];
+                    }
+                }
+                if (bSuccess) {
+                    // should verify it is there and of the correct type???
+                    NSDictionary* newEntry = [self getDirectoryEntry:newFullPath isDirectory:bSrcIsDir];  // should be the same type as source
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:newEntry];
+                } else {
+                    errCode = INVALID_MODIFICATION_ERR; // catch all
+                    if (error) {
+                        if (([error code] == NSFileReadUnknownError) || ([error code] == NSFileReadTooLargeError)) {
+                            errCode = NOT_READABLE_ERR;
+                        } else if ([error code] == NSFileWriteOutOfSpaceError) {
+                            errCode = QUOTA_EXCEEDED_ERR;
+                        } else if ([error code] == NSFileWriteNoPermissionError) {
+                            errCode = NO_MODIFICATION_ALLOWED_ERR;
+                        }
+                    }
+                }
+            }
+        }
+    }
+    if (errCode > 0) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:errCode];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* return the URI to the entry
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath of entry
+ *	1 - desired mime type of entry - ignored - always returns file://
+ */
+
+/*  Not needed since W3C toURI is synchronous.  Leaving code here for now in case W3C spec changes.....
+- (void) toURI:(CDVInvokedUrlCommand*)command
+{
+    NSString* callbackId = command.callbackId;
+    NSString* argPath = [command.arguments objectAtIndex:0];
+    PluginResult* result = nil;
+    NSString* jsString = nil;
+
+    NSString* fullPath = [self getFullPath: argPath];
+    if (fullPath) {
+        // do we need to make sure the file actually exists?
+        // create file uri
+        NSString* strUri = [fullPath stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
+        NSURL* fileUrl = [NSURL fileURLWithPath:strUri];
+        if (fileUrl) {
+            result = [PluginResult resultWithStatus:CDVCommandStatus_OK messageAsString: [fileUrl absoluteString]];
+            jsString = [result toSuccessCallbackString:callbackId];
+        } // else NOT_FOUND_ERR
+    }
+    if(!jsString) {
+        // was error
+        result = [PluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt: NOT_FOUND_ERR cast:  @"window.localFileSystem._castError"];
+        jsString = [result toErrorCallbackString:callbackId];
+    }
+
+    [self writeJavascript:jsString];
+}*/
+- (void)getFileMetadata:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command.arguments objectAtIndex:0];
+
+    // return unsupported result for assets-library URLs
+    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"getFileMetadata not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+
+    NSString* fullPath = argPath; // [self getFullPath: argPath];
+
+    if (fullPath) {
+        NSFileManager* fileMgr = [[NSFileManager alloc] init];
+        BOOL bIsDir = NO;
+        // make sure it exists and is not a directory
+        BOOL bExists = [fileMgr fileExistsAtPath:fullPath isDirectory:&bIsDir];
+        if (!bExists || bIsDir) {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+        } else {
+            // create dictionary of file info
+            NSError* __autoreleasing error = nil;
+            NSDictionary* fileAttrs = [fileMgr attributesOfItemAtPath:fullPath error:&error];
+            NSMutableDictionary* fileInfo = [NSMutableDictionary dictionaryWithCapacity:5];
+            [fileInfo setObject:[NSNumber numberWithUnsignedLongLong:[fileAttrs fileSize]] forKey:@"size"];
+            [fileInfo setObject:argPath forKey:@"fullPath"];
+            [fileInfo setObject:@"" forKey:@"type"];  // can't easily get the mimetype unless create URL, send request and read response so skipping
+            [fileInfo setObject:[argPath lastPathComponent] forKey:@"name"];
+            NSDate* modDate = [fileAttrs fileModificationDate];
+            NSNumber* msDate = [NSNumber numberWithDouble:[modDate timeIntervalSince1970] * 1000];
+            [fileInfo setObject:msDate forKey:@"lastModifiedDate"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:fileInfo];
+        }
+    }
+    if (!result) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_INSTANTIATION_EXCEPTION];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (void)readEntries:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* fullPath = [command.arguments objectAtIndex:0];
+
+    // return unsupported result for assets-library URLs
+    if ([fullPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readEntries not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+
+    NSFileManager* fileMgr = [[NSFileManager alloc] init];
+    NSError* __autoreleasing error = nil;
+    NSArray* contents = [fileMgr contentsOfDirectoryAtPath:fullPath error:&error];
+
+    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 = [fullPath stringByAppendingPathComponent:name];
+                BOOL bIsDir = NO;
+                [fileMgr fileExistsAtPath:entryPath isDirectory:&bIsDir];
+                NSDictionary* entryDict = [self getDirectoryEntry:entryPath isDirectory:bIsDir];
+                [entries addObject:entryDict];
+            }
+        }
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsArray:entries];
+    } else {
+        // assume not found but could check error for more specific error conditions
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    }
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* read and return file data
+ * IN:
+ * NSArray* arguments
+ *	0 - NSString* fullPath
+ *	1 - NSString* encoding - NOT USED,  iOS reads and writes using UTF8!
+ *	2 - NSString* start - OPTIONAL, only provided when not == 0.
+ *	3 - NSString* end - OPTIONAL, only provided when not == length.
+ */
+- (void)readAsText:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command.arguments objectAtIndex:0];
+    NSInteger start = 0;
+    NSInteger end = -1;
+
+    if ([command.arguments count] >= 3) {
+        start = [[command.arguments objectAtIndex:2] integerValue];
+    }
+    if ([command.arguments count] >= 4) {
+        end = [[command.arguments objectAtIndex:3] integerValue];
+    }
+
+    // return unsupported result for assets-library URLs
+    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readAsText not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    // NSString* encoding = [command.arguments objectAtIndex:2];   // not currently used
+    CDVPluginResult* result = nil;
+
+    NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:argPath];
+
+    if (!file) {
+        // invalid path entry
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+    } else {
+        if (start > 0) {
+            [file seekToFileOffset:start];
+        }
+
+        NSData* readData;
+        if (end < 0) {
+            readData = [file readDataToEndOfFile];
+        } else {
+            readData = [file readDataOfLength:(end - start)];
+        }
+
+        [file closeFile];
+        NSString* pNStrBuff = nil;
+        if (readData) {
+            pNStrBuff = [[NSString alloc] initWithBytes:[readData bytes] length:[readData length] encoding:NSUTF8StringEncoding];
+        } else {
+            // return empty string if no data
+            pNStrBuff = @"";
+        }
+
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:pNStrBuff];
+    }
+    [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
+ *
+ * Determines the mime type from the file extension, returns ENCODING_ERR if mimetype can not be determined.
+ */
+
+- (void)readAsDataURL:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command.arguments objectAtIndex:0];
+    NSInteger start = 0;
+    NSInteger end = -1;
+
+    if ([command.arguments count] >= 2) {
+        start = [[command.arguments objectAtIndex:1] integerValue];
+    }
+    if ([command.arguments count] >= 3) {
+        end = [[command.arguments objectAtIndex:2] integerValue];
+    }
+
+    // return unsupported result for assets-library URLs
+    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"readAsDataURL not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVFileError errCode = ABORT_ERR;
+    CDVPluginResult* result = nil;
+
+    if (!argPath) {
+        errCode = SYNTAX_ERR;
+    } else {
+        NSString* mimeType = [self getMimeTypeFromPath:argPath];
+        if (!mimeType) {
+            // can't return as data URL if can't figure out the mimeType
+            errCode = ENCODING_ERR;
+        } else {
+            NSFileHandle* file = [NSFileHandle fileHandleForReadingAtPath:argPath];
+            if (start > 0) {
+                [file seekToFileOffset:start];
+            }
+
+            NSData* readData;
+            if (end < 0) {
+                readData = [file readDataToEndOfFile];
+            } else {
+                readData = [file readDataOfLength:(end - start)];
+            }
+
+            [file closeFile];
+            if (readData) {
+                NSString* output = [NSString stringWithFormat:@"data:%@;base64,%@", mimeType, [readData base64EncodedString]];
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:output];
+            } else {
+                errCode = NOT_FOUND_ERR;
+            }
+        }
+    }
+    if (!result) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+    }
+    // NSLog(@"readAsDataURL return: %@", jsString);
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+/* helper function to get the mimeType from the file extension
+ * IN:
+ *	NSString* fullPath - filename (may include path)
+ * OUT:
+ *	NSString* the mime type as type/subtype.  nil if not able to determine
+ */
+- (NSString*)getMimeTypeFromPath:(NSString*)fullPath
+{
+    NSString* mimeType = nil;
+
+    if (fullPath) {
+        CFStringRef typeId = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)[fullPath pathExtension], NULL);
+        if (typeId) {
+            mimeType = (__bridge_transfer NSString*)UTTypeCopyPreferredTagWithClass(typeId, kUTTagClassMIMEType);
+            if (!mimeType) {
+                // special case for m4a
+                if ([(__bridge NSString*) typeId rangeOfString:@"m4a-audio"].location != NSNotFound) {
+                    mimeType = @"audio/mp4";
+                } else if ([[fullPath pathExtension] rangeOfString:@"wav"].location != NSNotFound) {
+                    mimeType = @"audio/wav";
+                }
+            }
+            CFRelease(typeId);
+        }
+    }
+    return mimeType;
+}
+
+- (void)truncate:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command.arguments objectAtIndex:0];
+    unsigned long long pos = (unsigned long long)[[command.arguments objectAtIndex:1] longLongValue];
+
+    // return unsupported result for assets-library URLs
+    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"truncate not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    NSString* appFile = argPath; // [self getFullPath:argPath];
+
+    unsigned long long newPos = [self truncateFile:appFile atPosition:pos];
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:newPos];
+
+    [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+}
+
+- (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;
+}
+
+/* write
+ * IN:
+ * NSArray* arguments
+ *  0 - NSString* file path to write to
+ *  1 - NSString* data to write
+ *  2 - NSNumber* position to begin writing
+ */
+- (void)write:(CDVInvokedUrlCommand*)command
+{
+    NSString* callbackId = command.callbackId;
+    NSArray* arguments = command.arguments;
+
+    // arguments
+    NSString* argPath = [arguments objectAtIndex:0];
+    NSString* argData = [arguments objectAtIndex:1];
+    unsigned long long pos = (unsigned long long)[[arguments objectAtIndex:2] longLongValue];
+
+    // return unsupported result for assets-library URLs
+    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"write not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    NSString* fullPath = argPath; // [self getFullPath:argPath];
+
+    [self truncateFile:fullPath atPosition:pos];
+
+    [self writeToFile:fullPath withData:argData append:YES callback:callbackId];
+}
+
+- (void)writeToFile:(NSString*)filePath withData:(NSString*)data append:(BOOL)shouldAppend callback:(NSString*)callbackId
+{
+    CDVPluginResult* result = nil;
+    CDVFileError errCode = INVALID_MODIFICATION_ERR;
+    int bytesWritten = 0;
+    NSData* encData = [data dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
+
+    if (filePath) {
+        NSOutputStream* fileStream = [NSOutputStream outputStreamToFileAtPath:filePath append:shouldAppend];
+        if (fileStream) {
+            NSUInteger len = [encData length];
+            [fileStream open];
+
+            bytesWritten = [fileStream write:[encData bytes] maxLength:len];
+
+            [fileStream close];
+            if (bytesWritten > 0) {
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:bytesWritten];
+                // } else {
+                // can probably get more detailed error info via [fileStream streamError]
+                // errCode already set to INVALID_MODIFICATION_ERR;
+                // bytesWritten = 0; // may be set to -1 on error
+            }
+        } // else fileStream not created return INVALID_MODIFICATION_ERR
+    } else {
+        // invalid filePath
+        errCode = NOT_FOUND_ERR;
+    }
+    if (!result) {
+        // was an error
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsInt:errCode];
+    }
+    [self.commandDelegate sendPluginResult:result callbackId:callbackId];
+}
+
+- (void)testFileExists:(CDVInvokedUrlCommand*)command
+{
+    // arguments
+    NSString* argPath = [command.arguments objectAtIndex: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.arguments objectAtIndex: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];
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h
new file mode 100644
index 0000000..5392236
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.h
@@ -0,0 +1,72 @@
+/*
+ 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 "CDVPlugin.h"
+
+enum CDVFileTransferError {
+    FILE_NOT_FOUND_ERR = 1,
+    INVALID_URL_ERR = 2,
+    CONNECTION_ERR = 3,
+    CONNECTION_ABORTED = 4
+};
+typedef int CDVFileTransferError;
+
+enum CDVFileTransferDirection {
+    CDV_TRANSFER_UPLOAD = 1,
+    CDV_TRANSFER_DOWNLOAD = 2,
+};
+typedef int CDVFileTransferDirection;
+
+// Magic value within the options dict used to set a cookie.
+extern NSString* const kOptionsKeyCookie;
+
+@interface CDVFileTransfer : CDVPlugin {}
+
+- (void)upload:(CDVInvokedUrlCommand*)command;
+- (void)download:(CDVInvokedUrlCommand*)command;
+- (NSString*)escapePathComponentForUrlString:(NSString*)urlString;
+
+// Visible for testing.
+- (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData:(NSData*)fileData;
+- (NSMutableDictionary*)createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target;
+
+- (NSMutableDictionary*)createFileTransferError:(int)code
+       AndSource                                   :(NSString*)source
+       AndTarget                                   :(NSString*)target
+   AndHttpStatus                               :(int)httpStatus;
+@property (readonly) NSMutableDictionary* activeTransfers;
+@end
+
+@interface CDVFileTransferDelegate : NSObject {}
+
+@property (strong) NSMutableData* responseData; // atomic
+@property (nonatomic, strong) CDVFileTransfer* command;
+@property (nonatomic, assign) CDVFileTransferDirection direction;
+@property (nonatomic, strong) NSURLConnection* connection;
+@property (nonatomic, copy) NSString* callbackId;
+@property (nonatomic, copy) NSString* objectId;
+@property (nonatomic, copy) NSString* source;
+@property (nonatomic, copy) NSString* target;
+@property (assign) int responseCode; // atomic
+@property (nonatomic, assign) NSInteger bytesTransfered;
+@property (nonatomic, assign) NSInteger bytesExpected;
+@property (nonatomic, assign) BOOL trustAllHosts;
+
+@end;

http://git-wip-us.apache.org/repos/asf/cordova-cli/blob/d61deccd/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m
----------------------------------------------------------------------
diff --git a/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m
new file mode 100644
index 0000000..128c954
--- /dev/null
+++ b/lib/cordova-ios/CordovaLib/Classes/CDVFileTransfer.m
@@ -0,0 +1,579 @@
+/*
+ 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 "CDV.h"
+
+#include <CFNetwork/CFNetwork.h>
+
+@interface CDVFileTransfer ()
+// Sets the requests headers for the request.
+- (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req;
+// Creates a delegate to handle an upload.
+- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command;
+// Creates an NSData* for the file for the given upload arguments.
+- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command;
+@end
+
+// Buffer size to use for streaming uploads.
+static const NSUInteger kStreamBufferSize = 32768;
+// Magic value within the options dict used to set a cookie.
+NSString* const kOptionsKeyCookie = @"__cookie";
+// Form boundary for multi-part requests.
+NSString* const kFormBoundary = @"+++++org.apache.cordova.formBoundary";
+
+// Writes the given data to the stream in a blocking way.
+// If successful, returns bytesToWrite.
+// If the stream was closed on the other end, returns 0.
+// If there was an error, returns -1.
+static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
+{
+    UInt8* bytes = (UInt8*)[data bytes];
+    NSUInteger bytesToWrite = [data length];
+    NSUInteger totalBytesWritten = 0;
+
+    while (totalBytesWritten < bytesToWrite) {
+        CFIndex result = CFWriteStreamWrite(stream,
+            bytes + totalBytesWritten,
+            bytesToWrite - totalBytesWritten);
+        if (result < 0) {
+            CFStreamError error = CFWriteStreamGetError(stream);
+            NSLog(@"WriteStreamError domain: %ld error: %ld", error.domain, error.error);
+            return result;
+        } else if (result == 0) {
+            return result;
+        }
+        totalBytesWritten += result;
+    }
+
+    return totalBytesWritten;
+}
+
+@implementation CDVFileTransfer
+@synthesize activeTransfers;
+
+- (NSString*)escapePathComponentForUrlString:(NSString*)urlString
+{
+    NSRange schemeAndHostRange = [urlString rangeOfString:@"://.*?/" options:NSRegularExpressionSearch];
+
+    if (schemeAndHostRange.length == 0) {
+        return urlString;
+    }
+
+    NSInteger schemeAndHostEndIndex = NSMaxRange(schemeAndHostRange);
+    NSString* schemeAndHost = [urlString substringToIndex:schemeAndHostEndIndex];
+    NSString* pathComponent = [urlString substringFromIndex:schemeAndHostEndIndex];
+    pathComponent = [pathComponent stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
+
+    return [schemeAndHost stringByAppendingString:pathComponent];
+}
+
+- (void)applyRequestHeaders:(NSDictionary*)headers toRequest:(NSMutableURLRequest*)req
+{
+    [req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"];
+
+    NSString* userAgent = [[self.webView request] valueForHTTPHeaderField:@"User-Agent"];
+    if (userAgent) {
+        [req setValue:userAgent forHTTPHeaderField:@"User-Agent"];
+    }
+
+    for (NSString* headerName in headers) {
+        id value = [headers objectForKey:headerName];
+        if (!value || (value == [NSNull null])) {
+            value = @"null";
+        }
+
+        // First, remove an existing header if one exists.
+        [req setValue:nil forHTTPHeaderField:headerName];
+
+        if (![value isKindOfClass:[NSArray class]]) {
+            value = [NSArray arrayWithObject:value];
+        }
+
+        // Then, append all header values.
+        for (id __strong subValue in value) {
+            // Convert from an NSNumber -> NSString.
+            if ([subValue respondsToSelector:@selector(stringValue)]) {
+                subValue = [subValue stringValue];
+            }
+            if ([subValue isKindOfClass:[NSString class]]) {
+                [req addValue:subValue forHTTPHeaderField:headerName];
+            }
+        }
+    }
+}
+
+- (NSURLRequest*)requestForUploadCommand:(CDVInvokedUrlCommand*)command fileData:(NSData*)fileData
+{
+    // arguments order from js: [filePath, server, fileKey, fileName, mimeType, params, debug, chunkedMode]
+    // however, params is a JavaScript object and during marshalling is put into the options dict,
+    // thus debug and chunkedMode are the 6th and 7th arguments
+    NSArray* arguments = command.arguments;
+    NSString* target = (NSString*)[arguments objectAtIndex:0];
+    NSString* server = (NSString*)[arguments objectAtIndex:1];
+    NSString* fileKey = [arguments objectAtIndex:2 withDefault:@"file"];
+    NSString* fileName = [arguments objectAtIndex:3 withDefault:@"no-filename"];
+    NSString* mimeType = [arguments objectAtIndex:4 withDefault:nil];
+    NSDictionary* options = [arguments objectAtIndex:5 withDefault:nil];
+    //    BOOL trustAllHosts = [[arguments objectAtIndex:6 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
+    BOOL chunkedMode = [[arguments objectAtIndex:7 withDefault:[NSNumber numberWithBool:YES]] boolValue];
+    NSDictionary* headers = [arguments objectAtIndex:8 withDefault:nil];
+
+    CDVPluginResult* result = nil;
+    CDVFileTransferError errorCode = 0;
+
+    // NSURL does not accepts URLs with spaces in the path. We escape the path in order
+    // to be more lenient.
+    NSURL* url = [NSURL URLWithString:server];
+
+    if (!url) {
+        errorCode = INVALID_URL_ERR;
+        NSLog(@"File Transfer Error: Invalid server URL %@", server);
+    } else if (!fileData) {
+        errorCode = FILE_NOT_FOUND_ERR;
+    }
+
+    if (errorCode > 0) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:target AndTarget:server]];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return nil;
+    }
+
+    NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
+    [req setHTTPMethod:@"POST"];
+
+    //    Magic value to set a cookie
+    if ([options objectForKey:kOptionsKeyCookie]) {
+        [req setValue:[options objectForKey:kOptionsKeyCookie] forHTTPHeaderField:@"Cookie"];
+        [req setHTTPShouldHandleCookies:NO];
+    }
+
+    NSString* contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", kFormBoundary];
+    [req setValue:contentType forHTTPHeaderField:@"Content-Type"];
+    [self applyRequestHeaders:headers toRequest:req];
+
+    NSData* formBoundaryData = [[NSString stringWithFormat:@"--%@\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding];
+    NSMutableData* postBodyBeforeFile = [NSMutableData data];
+
+    for (NSString* key in options) {
+        id val = [options objectForKey:key];
+        if (!val || (val == [NSNull null]) || [key isEqualToString:kOptionsKeyCookie]) {
+            continue;
+        }
+        // if it responds to stringValue selector (eg NSNumber) get the NSString
+        if ([val respondsToSelector:@selector(stringValue)]) {
+            val = [val stringValue];
+        }
+        // finally, check whether it is a NSString (for dataUsingEncoding selector below)
+        if (![val isKindOfClass:[NSString class]]) {
+            continue;
+        }
+
+        [postBodyBeforeFile appendData:formBoundaryData];
+        [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]];
+        [postBodyBeforeFile appendData:[val dataUsingEncoding:NSUTF8StringEncoding]];
+        [postBodyBeforeFile appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
+    }
+
+    [postBodyBeforeFile appendData:formBoundaryData];
+    [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\r\n", fileKey, fileName] dataUsingEncoding:NSUTF8StringEncoding]];
+    if (mimeType != nil) {
+        [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Type: %@\r\n", mimeType] dataUsingEncoding:NSUTF8StringEncoding]];
+    }
+    [postBodyBeforeFile appendData:[[NSString stringWithFormat:@"Content-Length: %d\r\n\r\n", [fileData length]] dataUsingEncoding:NSUTF8StringEncoding]];
+
+    DLog(@"fileData length: %d", [fileData length]);
+    NSData* postBodyAfterFile = [[NSString stringWithFormat:@"\r\n--%@--\r\n", kFormBoundary] dataUsingEncoding:NSUTF8StringEncoding];
+
+    NSUInteger totalPayloadLength = [postBodyBeforeFile length] + [fileData length] + [postBodyAfterFile length];
+    [req setValue:[[NSNumber numberWithInteger:totalPayloadLength] stringValue] forHTTPHeaderField:@"Content-Length"];
+
+    if (chunkedMode) {
+        CFReadStreamRef readStream = NULL;
+        CFWriteStreamRef writeStream = NULL;
+        CFStreamCreateBoundPair(NULL, &readStream, &writeStream, kStreamBufferSize);
+        [req setHTTPBodyStream:CFBridgingRelease(readStream)];
+
+        [self.commandDelegate runInBackground:^{
+                if (CFWriteStreamOpen (writeStream)) {
+                    NSData* chunks[] = {postBodyBeforeFile, fileData, postBodyAfterFile};
+                    int numChunks = sizeof (chunks) / sizeof (chunks[0]);
+
+                    for (int i = 0; i < numChunks; ++i) {
+                        CFIndex result = WriteDataToStream (chunks[i], writeStream);
+                        if (result <= 0) {
+                            break;
+                        }
+                    }
+                } else {
+                    NSLog (@"FileTransfer: Failed to open writeStream");
+                }
+                CFWriteStreamClose (writeStream);
+                CFRelease (writeStream);
+            }];
+    } else {
+        [postBodyBeforeFile appendData:fileData];
+        [postBodyBeforeFile appendData:postBodyAfterFile];
+        [req setHTTPBody:postBodyBeforeFile];
+    }
+    return req;
+}
+
+- (CDVFileTransferDelegate*)delegateForUploadCommand:(CDVInvokedUrlCommand*)command
+{
+    NSString* source = [command.arguments objectAtIndex:0];
+    NSString* server = [command.arguments objectAtIndex:1];
+    BOOL trustAllHosts = [[command.arguments objectAtIndex:6 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
+    NSString* objectId = [command.arguments objectAtIndex:9];
+
+    CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init];
+
+    delegate.command = self;
+    delegate.callbackId = command.callbackId;
+    delegate.direction = CDV_TRANSFER_UPLOAD;
+    delegate.objectId = objectId;
+    delegate.source = source;
+    delegate.target = server;
+    delegate.trustAllHosts = trustAllHosts;
+
+    return delegate;
+}
+
+- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command
+{
+    NSString* target = (NSString*)[command.arguments objectAtIndex:0];
+    NSError* __autoreleasing err = nil;
+    // Extract the path part out of a file: URL.
+    NSString* filePath = [target hasPrefix:@"/"] ? [target copy] : [[NSURL URLWithString:target] path];
+
+    // Memory map the file so that it can be read efficiently even if it is large.
+    NSData* fileData = [NSData dataWithContentsOfFile:filePath options:NSDataReadingMappedIfSafe error:&err];
+
+    if (err != nil) {
+        NSLog(@"Error opening file %@: %@", target, err);
+    }
+    return fileData;
+}
+
+- (void)upload:(CDVInvokedUrlCommand*)command
+{
+    NSString* argPath = [command.arguments objectAtIndex:0];
+
+    // return unsupported result for assets-library URLs
+    if ([argPath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"upload not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    // fileData and req are split into helper functions to ease the unit testing of delegateForUpload.
+    NSData* fileData = [self fileDataForUploadCommand:command];
+    NSURLRequest* req = [self requestForUploadCommand:command fileData:fileData];
+
+    if (req == nil) {
+        return;
+    }
+    CDVFileTransferDelegate* delegate = [self delegateForUploadCommand:command];
+    [NSURLConnection connectionWithRequest:req delegate:delegate];
+
+    if (activeTransfers == nil) {
+        activeTransfers = [[NSMutableDictionary alloc] init];
+    }
+
+    [activeTransfers setObject:delegate forKey:delegate.objectId];
+}
+
+- (void)abort:(CDVInvokedUrlCommand*)command
+{
+    NSString* objectId = [command.arguments objectAtIndex:0];
+
+    CDVFileTransferDelegate* delegate = [activeTransfers objectForKey:objectId];
+
+    if (delegate != nil) {
+        [delegate.connection cancel];
+        [activeTransfers removeObjectForKey:objectId];
+
+        // delete uncomplete file
+        NSFileManager* fileMgr = [NSFileManager defaultManager];
+        [fileMgr removeItemAtPath:delegate.target error:nil];
+
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:CONNECTION_ABORTED AndSource:delegate.source AndTarget:delegate.target]];
+        [self.commandDelegate sendPluginResult:result callbackId:delegate.callbackId];
+    }
+}
+
+- (void)download:(CDVInvokedUrlCommand*)command
+{
+    DLog(@"File Transfer downloading file...");
+    NSString* sourceUrl = [command.arguments objectAtIndex:0];
+    NSString* filePath = [command.arguments objectAtIndex:1];
+    BOOL trustAllHosts = [[command.arguments objectAtIndex:2 withDefault:[NSNumber numberWithBool:YES]] boolValue]; // allow self-signed certs
+    NSString* objectId = [command.arguments objectAtIndex:3];
+
+    // return unsupported result for assets-library URLs
+    if ([filePath hasPrefix:kCDVAssetsLibraryPrefix]) {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_MALFORMED_URL_EXCEPTION messageAsString:@"download not supported for assets-library URLs."];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    CDVPluginResult* result = nil;
+    CDVFileTransferError errorCode = 0;
+
+    NSURL* file;
+
+    if ([filePath hasPrefix:@"/"]) {
+        file = [NSURL fileURLWithPath:filePath];
+    } else {
+        file = [NSURL URLWithString:filePath];
+    }
+
+    NSURL* url = [NSURL URLWithString:sourceUrl];
+
+    if (!url) {
+        errorCode = INVALID_URL_ERR;
+        NSLog(@"File Transfer Error: Invalid server URL %@", sourceUrl);
+    } else if (![file isFileURL]) {
+        errorCode = FILE_NOT_FOUND_ERR;
+        NSLog(@"File Transfer Error: Invalid file path or URL %@", filePath);
+    }
+
+    if (errorCode > 0) {
+        result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[self createFileTransferError:errorCode AndSource:sourceUrl AndTarget:filePath]];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+        return;
+    }
+
+    NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
+    [self applyRequestHeaders:nil toRequest:req];
+
+    CDVFileTransferDelegate* delegate = [[CDVFileTransferDelegate alloc] init];
+    delegate.command = self;
+    delegate.direction = CDV_TRANSFER_DOWNLOAD;
+    delegate.callbackId = command.callbackId;
+    delegate.objectId = objectId;
+    delegate.source = sourceUrl;
+    delegate.target = filePath;
+    delegate.trustAllHosts = trustAllHosts;
+
+    delegate.connection = [NSURLConnection connectionWithRequest:req delegate:delegate];
+
+    if (activeTransfers == nil) {
+        activeTransfers = [[NSMutableDictionary alloc] init];
+    }
+
+    [activeTransfers setObject:delegate forKey:delegate.objectId];
+}
+
+- (NSMutableDictionary*)createFileTransferError:(int)code AndSource:(NSString*)source AndTarget:(NSString*)target
+{
+    NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:3];
+
+    [result setObject:[NSNumber numberWithInt:code] forKey:@"code"];
+    [result setObject:source forKey:@"source"];
+    [result setObject:target forKey:@"target"];
+    NSLog(@"FileTransferError %@", result);
+
+    return result;
+}
+
+- (NSMutableDictionary*)createFileTransferError:(int)code
+                                      AndSource:(NSString*)source
+                                      AndTarget:(NSString*)target
+                                  AndHttpStatus:(int)httpStatus
+{
+    NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:4];
+
+    [result setObject:[NSNumber numberWithInt:code] forKey:@"code"];
+    [result setObject:source forKey:@"source"];
+    [result setObject:target forKey:@"target"];
+    [result setObject:[NSNumber numberWithInt:httpStatus] forKey:@"http_status"];
+    NSLog(@"FileTransferError %@", result);
+
+    return result;
+}
+
+- (void)onReset
+{
+    for (CDVFileTransferDelegate* delegate in [activeTransfers allValues]) {
+        [delegate.connection cancel];
+    }
+
+    [activeTransfers removeAllObjects];
+}
+
+@end
+
+@implementation CDVFileTransferDelegate
+
+@synthesize callbackId, connection, source, target, responseData, command, bytesTransfered, bytesExpected, direction, responseCode, objectId;
+
+- (void)connectionDidFinishLoading:(NSURLConnection*)connection
+{
+    NSString* uploadResponse = nil;
+    BOOL downloadResponse;
+    NSMutableDictionary* uploadResult;
+    CDVPluginResult* result = nil;
+    NSError* __autoreleasing error = nil;
+    NSString* parentPath;
+    BOOL bDirRequest = NO;
+    CDVFile* file;
+
+    NSLog(@"File Transfer Finished with response code %d", self.responseCode);
+
+    if (self.direction == CDV_TRANSFER_UPLOAD) {
+        if ((self.responseCode >= 200) && (self.responseCode < 300)) {
+            // create dictionary to return FileUploadResult object
+            uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+            uploadResult = [NSMutableDictionary dictionaryWithCapacity:3];
+            if (uploadResponse != nil) {
+                [uploadResult setObject:uploadResponse forKey:@"response"];
+            }
+            [uploadResult setObject:[NSNumber numberWithInt:self.bytesTransfered] forKey:@"bytesSent"];
+            [uploadResult setObject:[NSNumber numberWithInt:self.responseCode] forKey:@"responseCode"];
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadResult];
+        } else {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+        }
+    }
+    if (self.direction == CDV_TRANSFER_DOWNLOAD) {
+        DLog(@"Write file %@", target);
+        // error=[[NSError alloc]init];
+
+        if ((self.responseCode >= 200) && (self.responseCode < 300)) {
+            @try {
+                parentPath = [self.target stringByDeletingLastPathComponent];
+
+                // check if the path exists => create directories if needed
+                if (![[NSFileManager defaultManager] fileExistsAtPath:parentPath]) {
+                    [[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:nil];
+                }
+
+                downloadResponse = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error];
+
+                if (downloadResponse == NO) {
+                    // send our results back
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+                } else {
+                    DLog(@"File Transfer Download success");
+
+                    file = [[CDVFile alloc] init];
+
+                    result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[file getDirectoryEntry:target isDirectory:bDirRequest]];
+                }
+            }
+            @catch(id exception) {
+                // jump back to main thread
+                result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+            }
+        } else {
+            result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+        }
+    }
+
+    [self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
+
+    // remove connection for activeTransfers
+    [command.activeTransfers removeObjectForKey:objectId];
+}
+
+- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
+{
+    // required for iOS 4.3, for some reason; response is
+    // a plain NSURLResponse, not the HTTP subclass
+    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
+        NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
+
+        self.responseCode = [httpResponse statusCode];
+        self.bytesExpected = [response expectedContentLength];
+    } else if ([response.URL isFileURL]) {
+        NSDictionary* attr = [[NSFileManager defaultManager] attributesOfItemAtPath:[response.URL path] error:nil];
+        self.responseCode = 200;
+        self.bytesExpected = [attr[NSFileSize] longLongValue];
+    } else {
+        self.responseCode = 200;
+        self.bytesExpected = NSURLResponseUnknownLength;
+    }
+}
+
+- (void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
+{
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+
+    NSLog(@"File Transfer Error: %@", [error localizedDescription]);
+
+    // remove connection for activeTransfers
+    [command.activeTransfers removeObjectForKey:objectId];
+    [self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
+}
+
+- (void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data
+{
+    self.bytesTransfered += data.length;
+    [self.responseData appendData:data];
+
+    if (self.direction == CDV_TRANSFER_DOWNLOAD) {
+        BOOL lengthComputable = (self.bytesExpected != NSURLResponseUnknownLength);
+        NSMutableDictionary* downloadProgress = [NSMutableDictionary dictionaryWithCapacity:3];
+        [downloadProgress setObject:[NSNumber numberWithBool:lengthComputable] forKey:@"lengthComputable"];
+        [downloadProgress setObject:[NSNumber numberWithInt:self.bytesTransfered] forKey:@"loaded"];
+        [downloadProgress setObject:[NSNumber numberWithInt:self.bytesExpected] forKey:@"total"];
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:downloadProgress];
+        [result setKeepCallbackAsBool:true];
+        [self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
+    }
+}
+
+- (void)connection:(NSURLConnection*)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
+{
+    if (self.direction == CDV_TRANSFER_UPLOAD) {
+        NSMutableDictionary* uploadProgress = [NSMutableDictionary dictionaryWithCapacity:3];
+
+        [uploadProgress setObject:[NSNumber numberWithBool:true] forKey:@"lengthComputable"];
+        [uploadProgress setObject:[NSNumber numberWithInt:totalBytesWritten] forKey:@"loaded"];
+        [uploadProgress setObject:[NSNumber numberWithInt:totalBytesExpectedToWrite] forKey:@"total"];
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:uploadProgress];
+        [result setKeepCallbackAsBool:true];
+        [self.command.commandDelegate sendPluginResult:result callbackId:callbackId];
+    }
+    self.bytesTransfered = totalBytesWritten;
+}
+
+// for self signed certificates
+- (void)connection:(NSURLConnection*)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
+{
+    if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
+        if (self.trustAllHosts) {
+            NSURLCredential* credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
+            [challenge.sender useCredential:credential forAuthenticationChallenge:challenge];
+        }
+        [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
+    } else {
+        [challenge.sender performDefaultHandlingForAuthenticationChallenge:challenge];
+    }
+}
+
+- (id)init
+{
+    if ((self = [super init])) {
+        self.responseData = [NSMutableData data];
+    }
+    return self;
+}
+
+@end;


Mime
View raw message