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