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