mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-15 20:10:30 +00:00
* Heartbeat rework * remove some debug loggings * stik mini tool poc * Saperate JITEnableContext into extensions and put each feature into their own files. Fix local pairing file is removed after switched to external device by directly loading the remote device's pairing file in place. * custom origin & xmlhttprequest * Fix some memory leak * allow background script access idevice (codes are from the old JSSupport branch) Tests are not carried out but most of the js api should work. Do we need to obfuscate some functions' name? * Fix JITEnableContext singleton initializer race condition * Fix instprox callback --------- Co-authored-by: Stephen B <158498287+StephenDev0@users.noreply.github.com>
191 lines
6.7 KiB
Objective-C
191 lines
6.7 KiB
Objective-C
//
|
|
// IDeviceJSBridgeNSData.m
|
|
// StikJIT
|
|
//
|
|
// Created by s s on 2025/4/25.
|
|
//
|
|
@import Foundation;
|
|
#import "JSSupport.h"
|
|
|
|
@implementation IDeviceJSBridge (NSData)
|
|
|
|
- (void)nsdata_freeWithBody:(NSDictionary *)body replyHandler:(nonnull void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int handleId = [body[@"handle"] intValue];
|
|
if (!dataPool[@(handleId)]) {
|
|
replyHandler(nil, @"Invalid NSData handle");
|
|
return;
|
|
}
|
|
bool ans = [self freeNSData:handleId];
|
|
replyHandler(@(ans), nil);
|
|
}
|
|
|
|
- (void)nsdata_readWithBody:(NSDictionary *)body replyHandler:(nonnull void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int handleId = [body[@"handle"] intValue];
|
|
if (!dataPool[@(handleId)]) {
|
|
replyHandler(nil, @"Invalid NSData handle");
|
|
return;
|
|
}
|
|
NSData* data = dataPool[@(handleId)];
|
|
NSString* ans = [data base64EncodedStringWithOptions:0];
|
|
replyHandler(ans, nil);
|
|
}
|
|
|
|
- (void)nsdata_read_rangeWithBody:(NSDictionary *)body replyHandler:(nonnull void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int handleId = [body[@"handle"] intValue];
|
|
if (!dataPool[@(handleId)]) {
|
|
replyHandler(nil, @"Invalid NSData handle");
|
|
return;
|
|
}
|
|
NSData* data = dataPool[@(handleId)];
|
|
NSUInteger begin = [body[@"begin"] intValue];
|
|
NSUInteger end = [body[@"end"] intValue];
|
|
if(begin < 0 || end >= [data length] || begin > end) {
|
|
replyHandler(nil, @"Invalid range");
|
|
return;
|
|
}
|
|
|
|
[data subdataWithRange:NSMakeRange(begin, end)];
|
|
NSString* ans = [data base64EncodedStringWithOptions:0];
|
|
replyHandler(ans, nil);
|
|
}
|
|
|
|
- (void)nsdata_get_sizeWithBody:(NSDictionary *)body replyHandler:(nonnull void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int handleId = [body[@"handle"] intValue];
|
|
if (!dataPool[@(handleId)]) {
|
|
replyHandler(nil, @"Invalid NSData handle");
|
|
return;
|
|
}
|
|
NSData* data = dataPool[@(handleId)];
|
|
NSUInteger ans = [data length];
|
|
replyHandler(@(ans), nil);
|
|
}
|
|
|
|
- (void)nsdata_createWithBody:(NSDictionary *)body replyHandler:(nonnull void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
NSString* dataStr = body[@"base64Data"];
|
|
if (![dataStr isKindOfClass:NSString.class]) {
|
|
replyHandler(nil, @"Invalid base64Data");
|
|
return;
|
|
}
|
|
|
|
NSData* data = [[NSData alloc] initWithBase64EncodedString:dataStr options:0];
|
|
if (!data) {
|
|
replyHandler(nil, @"Failed to decode base64Data");
|
|
return;
|
|
}
|
|
int ans = [self registerNSData:data];
|
|
replyHandler(@(ans), nil);
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@implementation IDeviceJSBridge (LocalFile)
|
|
|
|
- (void)local_file_openWithBody:(NSDictionary *)body replyHandler:(void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
NSString *relativePath = body[@"path"];
|
|
NSURL *miniToolDataURL = [[NSFileManager.defaultManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]
|
|
.lastObject URLByAppendingPathComponent:@"MiniToolData"] ;
|
|
BOOL isDir = false;
|
|
if(![NSFileManager.defaultManager fileExistsAtPath:miniToolDataURL.path isDirectory:&isDir] || !isDir) {
|
|
[NSFileManager.defaultManager removeItemAtURL:miniToolDataURL error:nil];
|
|
[NSFileManager.defaultManager createDirectoryAtURL:miniToolDataURL withIntermediateDirectories:YES attributes:@{} error:nil];
|
|
}
|
|
NSString* path = [miniToolDataURL.path stringByAppendingPathComponent:relativePath];
|
|
NSString *mode = body[@"mode"];
|
|
|
|
// Prevent path traversal attacks by ensuring the resolved path stays within MiniToolData directory
|
|
NSString *resolvedPath = [path stringByStandardizingPath];
|
|
NSString *baseDir = [miniToolDataURL.path stringByStandardizingPath];
|
|
if (![resolvedPath hasPrefix:baseDir]) {
|
|
replyHandler(nil, @"Path traversal is not allowed.");
|
|
return;
|
|
}
|
|
|
|
if (![path isKindOfClass:NSString.class] || ![mode isKindOfClass:NSString.class]) {
|
|
replyHandler(nil, @"Invalid file path or mode");
|
|
return;
|
|
}
|
|
|
|
FILE *file = fopen([path UTF8String], [mode UTF8String]);
|
|
if (!file) {
|
|
replyHandler(nil, @"Failed to open file");
|
|
return;
|
|
}
|
|
|
|
int handleId = [self registerIdeviceHandle:(void*)file freeFunc:(void *)fclose];
|
|
replyHandler(@(handleId), nil);
|
|
}
|
|
|
|
- (void)local_file_closeWithBody:(NSDictionary *)body replyHandler:(void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int fileId = [body[@"file"] intValue];
|
|
|
|
if (!handles[@(fileId)] || handles[@(fileId)].freeFunc != fclose) {
|
|
replyHandler(nil, @"Invalid file handle");
|
|
return;
|
|
}
|
|
|
|
FILE *file = handles[@(fileId)].handle;
|
|
fclose(file);
|
|
[handles removeObjectForKey:@(fileId)];
|
|
|
|
replyHandler(@(YES), nil);
|
|
}
|
|
|
|
- (void)local_file_get_sizeWithBody:(NSDictionary *)body replyHandler:(void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int fileId = [body[@"file"] intValue];
|
|
|
|
if (!handles[@(fileId)] || handles[@(fileId)].freeFunc != fclose) {
|
|
replyHandler(nil, @"Invalid file handle");
|
|
return;
|
|
}
|
|
|
|
FILE *file = handles[@(fileId)].handle;
|
|
fseek(file, 0, SEEK_END);
|
|
long size = ftell(file);
|
|
fseek(file, 0, SEEK_SET); // Reset position
|
|
replyHandler(@(size), nil);
|
|
}
|
|
|
|
- (void)local_file_read_chunkWithBody:(NSDictionary *)body replyHandler:(void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int fileId = [body[@"file"] intValue];
|
|
long offset = [body[@"offset"] longValue];
|
|
long length = [body[@"length"] longValue];
|
|
|
|
if (!handles[@(fileId)] || handles[@(fileId)].freeFunc != fclose) {
|
|
replyHandler(nil, @"Invalid file handle");
|
|
return;
|
|
}
|
|
|
|
FILE *file = handles[@(fileId)].handle;
|
|
fseek(file, offset, SEEK_SET);
|
|
void *buffer = malloc(length);
|
|
size_t read = fread(buffer, 1, length, file);
|
|
|
|
NSData *data = [NSData dataWithBytesNoCopy:buffer length:read freeWhenDone:YES];
|
|
int dataHandleId = [self registerNSData:data];
|
|
replyHandler(@(dataHandleId), nil);
|
|
}
|
|
|
|
- (void)local_file_write_chunkWithBody:(NSDictionary *)body replyHandler:(void (^)(id _Nullable, NSString * _Nullable))replyHandler {
|
|
int fileId = [body[@"file"] intValue];
|
|
int dataId = [body[@"data"] intValue];
|
|
long offset = [body[@"offset"] longValue];
|
|
|
|
if (!handles[@(fileId)] || handles[@(fileId)].freeFunc != fclose) {
|
|
replyHandler(nil, @"Invalid file handle");
|
|
return;
|
|
}
|
|
if (!dataPool[@(dataId)]) {
|
|
replyHandler(nil, @"Invalid data handle");
|
|
return;
|
|
}
|
|
|
|
FILE *file = handles[@(fileId)].handle;
|
|
NSData *data = dataPool[@(dataId)];
|
|
fseek(file, offset, SEEK_SET);
|
|
size_t written = fwrite(data.bytes, 1, data.length, file);
|
|
|
|
replyHandler(@(written), nil);
|
|
}
|
|
|
|
@end
|