PageRenderTime 60ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/front/plugins/cordova-plugin-file/src/osx/CDVFile.m

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