Skip to content

Commit

Permalink
fix(ios): avoid crash when openStream fails (#11308)
Browse files Browse the repository at this point in the history
* fix(ios): avoid crash when openStream fails

wraps in try/catch to properly set exception on JS context
if JS code is in async callback/event/timer and has no try/catch it will still crash
Also, differs from 'old' procies in that the uncaught exception handler code isn't invoked

Fixes TIMOB-27515

* feat(ios): improve api compatibility for encrypted files

- add createdAt(), return Date equivalent of 0 timestamp
- add modifiedAt(), return Date equivalent of 0 timestamp
- make getDirectoryListing() return null
- attempt to have spaceAvailable() return a value by asking parent dir's proxy to retrieve it
- allow open() with TI_READ
- implement copy() (with encrypted file as source)
- implement parent property
- implement size property

* test(file): if deployType==='test' no timestamps of encrypted files

* refactor(ios): Ti.Filesystem.openStream calls getFile().open()

* build: allow passing along deploy type to test command

* ci(jenkins): use deploy type of test for unit test suite

* fix(ios): retain filepath for encrypted file blobs

* fix(ios): don't check for encrypted html or css files, should be none
  • Loading branch information
sgtcoolguy authored and lokeshchdhry committed Nov 5, 2019
1 parent 8599d4e commit d16b69f
Show file tree
Hide file tree
Showing 8 changed files with 963 additions and 34 deletions.
4 changes: 2 additions & 2 deletions Jenkinsfile
Expand Up @@ -104,7 +104,7 @@ def unitTests(os, nodeVersion, npmVersion, testSuiteBranch, testOnDevices) {
try {
if ('ios'.equals(os)) {
timeout(20) {
sh "node test.js -b ../../${zipName} -p ${os}"
sh "node test.js -D test -b ../../${zipName} -p ${os}"
}
} else {
timeout(30) {
Expand All @@ -113,7 +113,7 @@ def unitTests(os, nodeVersion, npmVersion, testSuiteBranch, testOnDevices) {
sh "node test.js -T device -C all -b ../../${zipName} -p ${os}"
// run PR tests on emulator
} else {
sh "node test.js -T emulator -C android-28-playstore-x86 -b ../../${zipName} -p ${os}"
sh "node test.js -T emulator -D test -C android-28-playstore-x86 -b ../../${zipName} -p ${os}"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion build/lib/test.js
Expand Up @@ -44,7 +44,7 @@ async function runTests(zipfile, platforms, program) {
const test = promisify(tests.test);

// Run the tests
return test(zipfile, platforms, program.target, program.deviceId, program.skipSdkInstall, undefined, undefined);
return test(zipfile, platforms, program.target, program.deviceId, program.skipSdkInstall, undefined, undefined, program.deployType);
}

async function outputResults(results) {
Expand Down
1 change: 1 addition & 0 deletions build/scons-test.js
Expand Up @@ -9,6 +9,7 @@ program
.option('-s, --skip-sdk-install', 'Skip the SDK installation step')
.option('-b, --branch [branch]', 'Which branch of the test suite to use', 'master')
.option('-v, --sdk-version [version]', 'Override the SDK version we report', process.env.PRODUCT_VERSION || version)
.option('-D, --deploy-type <type>', 'Override the deploy type used to build the project', /^(development|test)$/)
.parse(process.argv);

async function main(program) {
Expand Down
34 changes: 24 additions & 10 deletions iphone/Classes/FilesystemModule.m
Expand Up @@ -77,12 +77,21 @@ - (JSValue *)openStream:(TiStreamMode)mode

// allow variadic file components to be passed
NSString *resolvedPath = [self pathFromComponents:[args subarrayWithRange:NSMakeRange(1, [args count] - 1)]];
NSArray *payload = [NSArray arrayWithObjects:resolvedPath, [NSNumber numberWithInt:mode], nil];

KrollContext *context = GetKrollContext([[JSContext currentContext] JSGlobalContextRef]);
KrollBridge *ourBridge = (KrollBridge *)[context delegate];
id file = [[[TiFilesystemFileStreamProxy alloc] _initWithPageContext:ourBridge args:payload] autorelease];
return [self NativeToJSValue:file];
@try {
// Let's re-use code! we're effectively calling: getFile(path).open(mode);
TiFile *fileProxy = [self getFileProxy:resolvedPath];
if (fileProxy != nil) {
NSArray *payload = @[ [NSNumber numberWithInt:mode] ];
TiStreamProxy *streamProxy = [fileProxy open:payload];
if (streamProxy != nil) {
return [self NativeToJSValue:streamProxy];
}
}
} @catch (NSException *exception) {
JSValue *jsException = [self NativeToJSValue:exception];
[[JSContext currentContext] setException:jsException];
}
return nil;
}

- (TiStreamMode)MODE_APPEND
Expand Down Expand Up @@ -171,16 +180,21 @@ - (JSValue *)getFile
{
NSArray *args = [JSContext currentArguments];
NSString *newpath = [self pathFromComponents:args];
TiFile *fileProxy = [self getFileProxy:newpath];
return [self NativeToJSValue:fileProxy];
}

if ([newpath hasPrefix:[self resourcesDirectory]] && ([newpath hasSuffix:@".html"] || [newpath hasSuffix:@".js"] || [newpath hasSuffix:@".css"] || [newpath hasSuffix:@".json"])) {
NSURL *url = [NSURL fileURLWithPath:newpath];
- (TiFile *)getFileProxy:(NSString *)path
{
if ([path hasPrefix:[self resourcesDirectory]] && ([path hasSuffix:@".js"] || [path hasSuffix:@".json"])) {
NSURL *url = [NSURL fileURLWithPath:path];
NSData *data = [TiUtils loadAppResource:url];
if (data != nil) {
return [self NativeToJSValue:[[[TiFilesystemBlobProxy alloc] initWithURL:url data:data] autorelease]];
return [[[TiFilesystemBlobProxy alloc] initWithURL:url data:data] autorelease];
}
}

return [self NativeToJSValue:[[[TiFilesystemFileProxy alloc] initWithFile:newpath] autorelease]];
return [[[TiFilesystemFileProxy alloc] initWithFile:path] autorelease];
}

- (TiBlob *)getAsset
Expand Down
125 changes: 104 additions & 21 deletions iphone/Classes/TiFilesystemBlobProxy.m
Expand Up @@ -11,8 +11,14 @@

#import <TitaniumKit/Mimetypes.h>
#import <TitaniumKit/TiBlob.h>
#import <TitaniumKit/TiFilesystemFileProxy.h>
#import <TitaniumKit/TiUtils.h>

#import "TiDataStream.h"

#define FILE_TOSTR(x) \
([x isKindOfClass:[TiFilesystemFileProxy class]]) ? [(TiFilesystemFileProxy *)x nativePath] : [TiUtils stringValue:x]

@implementation TiFilesystemBlobProxy

- (id)initWithURL:(NSURL *)url_ data:(NSData *)data_
Expand Down Expand Up @@ -87,80 +93,152 @@ -(id)name \
FILENOOP(setHidden
: (id)x);

- (id)createTimestamp:(id)args
- (NSDate *)createTimestamp:(id)unused
{
return NUMBOOL(NO);
DEPRECATED_REPLACED(@"Filesystem.File.createTimestamp()", @"7.3.0", @"Filesystem.File.createdAt()");
return [self createdAt:unused];
}

- (id)modificationTimestamp:(id)args
- (NSDate *)createdAt:(id)unused
{
return NUMBOOL(NO);
return [NSDate dateWithTimeIntervalSince1970:0];
}

- (id)getDirectoryListing:(id)args
- (NSDate *)modificationTimestamp:(id)unused
{
return [NSArray array];
DEPRECATED_REPLACED(@"Filesystem.File.modificationTimestamp()", @"7.3.0", @"Filesystem.File.modifiedAt()");
return [self modifiedAt:nil];
}

- (id)spaceAvailable:(id)args
- (NSDate *)modifiedAt:(id)unused
{
return NUMBOOL(NO);
return [NSDate dateWithTimeIntervalSince1970:0];
}

- (NSArray *)getDirectoryListing:(id)args
{
return nil;
}

- (NSNumber *)spaceAvailable:(id)unused
{
id parent = [self parent];
if (parent != nil) {
return [parent spaceAvailable:nil];
}
return @(0);
}

- (id)createDirectory:(id)args
- (NSNumber *)createDirectory:(id)args
{
return NUMBOOL(NO);
}

- (id)createFile:(id)args
- (NSNumber *)createFile:(id)args
{
return NUMBOOL(NO);
}

- (id)deleteDirectory:(id)args
- (NSNumber *)deleteDirectory:(id)args
{
return NUMBOOL(NO);
}

- (id)deleteFile:(id)args
- (NSNumber *)deleteFile:(id)args
{
return NUMBOOL(NO);
}

- (id)move:(id)args
- (NSNumber *)move:(id)args
{
return NUMBOOL(NO);
}

- (id)rename:(id)args
- (NSNumber *)rename:(id)args
{
return NUMBOOL(NO);
}

- (TiBlob *)read:(id)args
{
NSString *mimetype = [Mimetypes mimeTypeForExtension:[[url path] lastPathComponent]];
return [[[TiBlob alloc] initWithData:data mimetype:mimetype] autorelease];
return [[[TiBlob alloc] initWithData:data andPath:[self nativePath]] autorelease];
}

- (TiStreamProxy *)open:(id)args
{
TiStreamMode mode;
ENSURE_INT_AT_INDEX(mode, args, 0);

if (mode != TI_READ) {
[self throwException:@"TypeError"
subreason:[NSString stringWithFormat:@"Invalid mode value %d", mode]
location:CODELOCATION];
}

TiDataStream *stream = [[[TiDataStream alloc] _initWithPageContext:[self executionContext]] autorelease];
[stream setData:data];
[stream setMode:mode];

return stream;
}

- (id)append:(id)args
- (NSString *)_grabFirstArgumentAsFileName_:(id)args
{
NSString *arg = [args objectAtIndex:0];
NSString *file = FILE_TOSTR(arg);
NSURL *fileUrl = [NSURL URLWithString:file];
if ([fileUrl isFileURL]) {
file = [fileUrl path];
}
NSString *dest = [file stringByStandardizingPath];
return dest;
}

// Xcode complains about the "copy" method naming, but in this case it's a proxy method so we are fine
#ifndef __clang_analyzer__
- (NSNumber *)copy:(id)args
{
ENSURE_TYPE(args, NSArray);

NSString *dest = [self _grabFirstArgumentAsFileName_:args];
if (![dest isAbsolutePath]) {
NSString *subpath = [path stringByDeletingLastPathComponent];
dest = [subpath stringByAppendingPathComponent:dest];
}

NSError *err = nil;
[data writeToFile:dest options:NSDataWritingFileProtectionComplete | NSDataWritingAtomic error:&err];
if (err != nil) {
NSLog(@"[ERROR] Could not write data to file at path \"%@\" - details: %@", dest, err);
}
return NUMBOOL(err == nil);
}
#endif

- (NSNumber *)append:(id)args
{
return NUMBOOL(NO);
}

- (id)write:(id)args
- (NSNumber *)write:(id)args
{
return NUMBOOL(NO);
}

- (id)extension:(id)args
- (NSString *)extension:(id)args
{
return [path pathExtension];
}

- (id)getParent:(id)args
- (NSString *)getParent:(id)args
{
return nil;
DEPRECATED_REPLACED(@"Filesystem.File.getParent()", @"7.0.0", @"Filesystem.File.parent");
return [path stringByDeletingLastPathComponent];
}

- (TiFilesystemFileProxy *)parent
{
return [[[TiFilesystemFileProxy alloc] initWithFile:[path stringByDeletingLastPathComponent]] autorelease];
}

- (id)name
Expand All @@ -178,6 +256,11 @@ - (id)description
return path;
}

- (unsigned long long)size
{
return data.length;
}

@end

#endif
7 changes: 7 additions & 0 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiBlob.h
Expand Up @@ -149,6 +149,13 @@ Initialize the blob with a system image.
*/
- (id)initWithData:(NSData *)data_ mimetype:(NSString *)mimetype_;

/**
Initialize the blob with data. Used for encrypted files/assets.
@param data_ The raw data.
@param path_ The path to the file.
*/
- (id)initWithData:(NSData *)data_ andPath:(NSString *)path_;

/**
Initialize the blob with contents of a file.
@param path The path to the file.
Expand Down
11 changes: 11 additions & 0 deletions iphone/TitaniumKit/TitaniumKit/Sources/API/TiBlob.m
Expand Up @@ -169,6 +169,17 @@ - (id)initWithData:(NSData *)data_ mimetype:(NSString *)mimetype_
return self;
}

- (id)initWithData:(NSData *)data_ andPath:(NSString *)path_
{
if (self = [super init]) {
data = [data_ retain];
type = TiBlobTypeData;
path = [path_ retain];
mimetype = [[Mimetypes mimeTypeForExtension:path] copy];
}
return self;
}

- (id)initWithFile:(NSString *)path_
{
if (self = [super init]) {
Expand Down

0 comments on commit d16b69f

Please sign in to comment.