aboutsummaryrefslogtreecommitdiffstats
path: root/iPhone/CordovaLib/Classes/CDVFileTransfer.m
diff options
context:
space:
mode:
Diffstat (limited to 'iPhone/CordovaLib/Classes/CDVFileTransfer.m')
-rwxr-xr-xiPhone/CordovaLib/Classes/CDVFileTransfer.m173
1 files changed, 108 insertions, 65 deletions
diff --git a/iPhone/CordovaLib/Classes/CDVFileTransfer.m b/iPhone/CordovaLib/Classes/CDVFileTransfer.m
index b25177a..4ccdce6 100755
--- a/iPhone/CordovaLib/Classes/CDVFileTransfer.m
+++ b/iPhone/CordovaLib/Classes/CDVFileTransfer.m
@@ -19,7 +19,10 @@
#import "CDV.h"
-#include <CFNetwork/CFNetwork.h>
+#import <AssetsLibrary/ALAsset.h>
+#import <AssetsLibrary/ALAssetRepresentation.h>
+#import <AssetsLibrary/ALAssetsLibrary.h>
+#import <CFNetwork/CFNetwork.h>
@interface CDVFileTransfer ()
// Sets the requests headers for the request.
@@ -27,7 +30,7 @@
// 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;
+- (void)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command;
@end
// Buffer size to use for streaming uploads.
@@ -87,7 +90,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
{
[req setValue:@"XMLHttpRequest" forHTTPHeaderField:@"X-Requested-With"];
- NSString* userAgent = [[self.webView request] valueForHTTPHeaderField:@"User-Agent"];
+ NSString* userAgent = [self.commandDelegate userAgent];
if (userAgent) {
[req setValue:userAgent forHTTPHeaderField:@"User-Agent"];
}
@@ -130,17 +133,10 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
NSString* fileName = [arguments objectAtIndex:3 withDefault:@"no-filename"];
NSString* mimeType = [arguments objectAtIndex:4 withDefault:nil];
NSDictionary* options = [arguments objectAtIndex:5 withDefault:nil];
- // NSString* trustAllHosts = (NSString*)[arguments objectAtIndex:6]; // allow self-signed certs
+ // 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];
- // CFStreamCreateBoundPair crashes on iOS < 5.
- if (!IsAtLeastiOSVersion(@"5")) {
- // TODO(agrieve): See if it's okay license-wise to include the work-around code from:
- // http://developer.apple.com/library/ios/#samplecode/SimpleURLConnections/Listings/PostController_m.html
- chunkedMode = NO;
- }
-
CDVPluginResult* result = nil;
CDVFileTransferError errorCode = 0;
@@ -245,6 +241,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
{
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];
@@ -255,29 +252,65 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
delegate.objectId = objectId;
delegate.source = source;
delegate.target = server;
+ delegate.trustAllHosts = trustAllHosts;
+
return delegate;
}
-- (NSData*)fileDataForUploadCommand:(CDVInvokedUrlCommand*)command
+- (void)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];
+ // return unsupported result for assets-library URLs
+ if ([target hasPrefix:kCDVAssetsLibraryPrefix]) {
+ // Instead, we return after calling the asynchronous method and send `result` in each of the blocks.
+ ALAssetsLibraryAssetForURLResultBlock resultBlock = ^(ALAsset * asset) {
+ if (asset) {
+ // We have the asset! Get the data and send it off.
+ ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
+ Byte* buffer = (Byte*)malloc ([assetRepresentation size]);
+ NSUInteger bufferSize = [assetRepresentation getBytes:buffer fromOffset:0.0 length:[assetRepresentation size] error:nil];
+ NSData* fileData = [NSData dataWithBytesNoCopy:buffer length:bufferSize freeWhenDone:YES];
+ [self uploadData:fileData command:command];
+ } else {
+ // We couldn't find the asset. Send the appropriate error.
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsInt:NOT_FOUND_ERR];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ }
+ };
+ ALAssetsLibraryAccessFailureBlock failureBlock = ^(NSError * error) {
+ // Retrieving the asset failed for some reason. Send the appropriate error.
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
+ [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+ };
+
+ ALAssetsLibrary* assetsLibrary = [[ALAssetsLibrary alloc] init];
+ [assetsLibrary assetForURL:[NSURL URLWithString:target] resultBlock:resultBlock failureBlock:failureBlock];
+ return;
+ } else {
+ // 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);
+ if (err != nil) {
+ NSLog (@"Error opening file %@: %@", target, err);
+ }
+ [self uploadData:fileData command:command];
}
- return fileData;
}
- (void)upload:(CDVInvokedUrlCommand*)command
{
// fileData and req are split into helper functions to ease the unit testing of delegateForUpload.
- NSData* fileData = [self fileDataForUploadCommand:command];
+ // First, get the file data. This method will call `uploadData:command`.
+ [self fileDataForUploadCommand:command];
+}
+
+- (void)uploadData:(NSData*)fileData command:(CDVInvokedUrlCommand*)command
+{
NSURLRequest* req = [self requestForUploadCommand:command fileData:fileData];
if (req == nil) {
@@ -302,9 +335,9 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
if (delegate != nil) {
[delegate.connection cancel];
[activeTransfers removeObjectForKey:objectId];
-
- //delete uncomplete file
- NSFileManager *fileMgr = [NSFileManager defaultManager];
+
+ // 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]];
@@ -317,8 +350,16 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
DLog(@"File Transfer downloading file...");
NSString* sourceUrl = [command.arguments objectAtIndex:0];
NSString* filePath = [command.arguments objectAtIndex:1];
- // NSString* trustAllHosts = (NSString*)[arguments objectAtIndex:6]; // allow self-signed certs
+ 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;
@@ -356,6 +397,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
delegate.objectId = objectId;
delegate.source = sourceUrl;
delegate.target = filePath;
+ delegate.trustAllHosts = trustAllHosts;
delegate.connection = [NSURLConnection connectionWithRequest:req delegate:delegate];
@@ -382,13 +424,15 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
AndSource:(NSString*)source
AndTarget:(NSString*)target
AndHttpStatus:(int)httpStatus
+ AndBody:(NSString*)body
{
- NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:4];
+ NSMutableDictionary* result = [NSMutableDictionary dictionaryWithCapacity:5];
[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"];
+ [result setObject:body forKey:@"body"];
NSLog(@"FileTransferError %@", result);
return result;
@@ -412,7 +456,8 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
- (void)connectionDidFinishLoading:(NSURLConnection*)connection
{
NSString* uploadResponse = nil;
- BOOL downloadResponse;
+ NSString* downloadResponse = nil;
+ BOOL downloadWriteOK = NO;
NSMutableDictionary* uploadResult;
CDVPluginResult* result = nil;
NSError* __autoreleasing error = nil;
@@ -423,9 +468,10 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
NSLog(@"File Transfer Finished with response code %d", self.responseCode);
if (self.direction == CDV_TRANSFER_UPLOAD) {
+ uploadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+
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"];
@@ -434,7 +480,7 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
[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]];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:uploadResponse]];
}
}
if (self.direction == CDV_TRANSFER_DOWNLOAD) {
@@ -450,11 +496,12 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
[[NSFileManager defaultManager] createDirectoryAtPath:parentPath withIntermediateDirectories:YES attributes:nil error:nil];
}
- downloadResponse = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error];
+ downloadWriteOK = [self.responseData writeToFile:self.target options:NSDataWritingFileProtectionNone error:&error];
- if (downloadResponse == NO) {
+ if (downloadWriteOK == NO) {
// send our results back
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+ downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:INVALID_URL_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
} else {
DLog(@"File Transfer Download success");
@@ -465,10 +512,13 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
}
@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]];
+ downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsDictionary:[command createFileTransferError:FILE_NOT_FOUND_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
}
} else {
- result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode]];
+ downloadResponse = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+
+ result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:downloadResponse]];
}
}
@@ -480,23 +530,29 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
- (void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
{
+ self.mimeType = [response MIMEType];
+
// required for iOS 4.3, for some reason; response is
// a plain NSURLResponse, not the HTTP subclass
- if (![response isKindOfClass:[NSHTTPURLResponse class]]) {
- self.responseCode = 403;
+ if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
+ NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
+
+ self.responseCode = [httpResponse statusCode];
self.bytesExpected = [response expectedContentLength];
- return;
+ } 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;
}
-
- NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response;
-
- self.responseCode = [httpResponse statusCode];
- self.bytesExpected = [response expectedContentLength];
}
- (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]];
+ NSString* body = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
+ CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:[command createFileTransferError:CONNECTION_ERR AndSource:source AndTarget:target AndHttpStatus:self.responseCode AndBody:body]];
NSLog(@"File Transfer Error: %@", [error localizedDescription]);
@@ -537,33 +593,20 @@ static CFIndex WriteDataToStream(NSData* data, CFWriteStreamRef stream)
self.bytesTransfered = totalBytesWritten;
}
-/* TESTING ONLY CODE
-// use ONLY for testing with self signed certificates
-// uncomment and modify server name in connection didReceiveAuthenticationChallenge
-- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
+// for self signed certificates
+- (void)connection:(NSURLConnection*)connection willSendRequestForAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
{
- return [protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust];
-}
-
-- (void)connection:(NSURLConnection *) connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge*)challenge
-{
- if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
- {
- //NSLog(@"challenge host: %@", challenge.protectionSpace.host);
- // we only trust our own domain
- if ([challenge.protectionSpace.host isEqualToString:@"serverName.domain.com"]){
- NSURLCredential* myCredential = [NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust];
-
- [challenge.sender useCredential:myCredential forAuthenticationChallenge: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];
}
-
- [challenge.sender continueWithoutCredentialForAuthenticationChallenge:challenge];
}
-// uncomment the above two methods for testing servers with self signed certificates
-// END TESTING ONLY CODE
- */
+
- (id)init
{
if ((self = [super init])) {