From e85614a2550b7864148bf28486334d5c866e66f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Thu, 27 Aug 2026 00:25:20 +0200 Subject: [PATCH] Clean up and export metadata --- CanardDumper/Makefile | 2 +- CanardDumper/Tweak.x | 264 +++++++++++++++++++++++++++++++----------- 2 files changed, 197 insertions(+), 69 deletions(-) diff --git a/CanardDumper/Makefile b/CanardDumper/Makefile index 8079fcc..967c033 100644 --- a/CanardDumper/Makefile +++ b/CanardDumper/Makefile @@ -1,5 +1,5 @@ TARGET = iphone:latest:15.0 -ARCHS = arm64 arm64e +ARCHS = arm64 INSTALL_TARGET_PROCESSES = fr.lecanardenchaine.app include $(THEOS)/makefiles/common.mk diff --git a/CanardDumper/Tweak.x b/CanardDumper/Tweak.x index 53a8f3b..a84f3cf 100644 --- a/CanardDumper/Tweak.x +++ b/CanardDumper/Tweak.x @@ -1,3 +1,5 @@ +// log stream --predicate 'process == "LeCanardEnchaine" AND eventMessage contains "CanardDumper" ' --level default --style compact + #import #import #import @@ -6,56 +8,130 @@ static NSString *const kCanardLogTag = @"[CanardDumper]"; static NSString *const kCanardDumpDirectoryName = @"CanardDumps"; +static NSString *const kCanardReaderFirstOpenKey = @"READER_FIRST_OPEN"; +static NSString *const kCanardFirebaseSessionsSettingsKey = @"firebase-sessions-settings"; +static NSString *const kCanardCatalogManagerDefaultsDomain = @"com.immanens.datamanager.CatalogManager.default"; + +static NSString *const kCanardFirebaseCollectAnrsKey = @"collect_anrs"; +static NSString *const kCanardFirebaseCollectBuildIdsKey = @"collect_build_ids"; +static NSString *const kCanardFirebaseCollectLoggedExceptionsKey = @"collect_logged_exceptions"; +static NSString *const kCanardFirebaseCollectReportsKey = @"collect_reports"; +static NSString *const kCanardFirebaseSessionsEnabledKey = @"sessions_enabled"; +static NSString *const kCanardCatalogLocalKey = @"local"; #pragma mark - State static NSString *CanardArchivePassword; -static int CanardLastSavedReadSize = -1; +static NSString *CanardCurrentArchivePath; +static NSString *CanardCurrentArchiveFileName; +static NSDictionary *CanardCurrentArchiveEntry; +static NSString *CanardLastSavedToken; -#pragma mark - UI Helpers +static NSDictionary *CanardForcedFirebaseSessionsSettings(NSDictionary *currentSettings) { + NSMutableDictionary *settings = currentSettings ? [currentSettings mutableCopy] : [NSMutableDictionary dictionary]; -static UIViewController *CanardTopViewController(UIViewController *viewController) { - if (!viewController) return nil; + settings[kCanardFirebaseCollectAnrsKey] = @NO; + settings[kCanardFirebaseCollectBuildIdsKey] = @NO; + settings[kCanardFirebaseCollectLoggedExceptionsKey] = @NO; + settings[kCanardFirebaseCollectReportsKey] = @NO; + settings[kCanardFirebaseSessionsEnabledKey] = @NO; + + return settings; +} + +static void CanardApplyForcedDefaults(void) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setBool:NO forKey:kCanardReaderFirstOpenKey]; + + NSDictionary *existing = [defaults dictionaryForKey:kCanardFirebaseSessionsSettingsKey]; + NSDictionary *forced = CanardForcedFirebaseSessionsSettings(existing); + [defaults setObject:forced forKey:kCanardFirebaseSessionsSettingsKey]; + [defaults synchronize]; + + NSLog(@"%@ Forced defaults applied for %@ and %@", kCanardLogTag, kCanardReaderFirstOpenKey, kCanardFirebaseSessionsSettingsKey); +} + +static NSArray *CanardCatalogLocalEntries(void) { + NSDictionary *domainValues = [[NSUserDefaults standardUserDefaults] + persistentDomainForName:kCanardCatalogManagerDefaultsDomain]; + id local = domainValues[kCanardCatalogLocalKey]; + if ([local isKindOfClass:[NSArray class]]) { + return (NSArray *)local; + } + return @[]; +} + +static NSDictionary *CanardCatalogEntryForFileName(NSString *fileName) { + if (fileName.length == 0) return nil; + + for (id item in CanardCatalogLocalEntries()) { + if (![item isKindOfClass:[NSDictionary class]]) { + continue; + } + + NSDictionary *entry = (NSDictionary *)item; + NSString *entryFileName = entry[@"fileName"]; + if ([entryFileName isKindOfClass:[NSString class]] && [entryFileName isEqualToString:fileName]) { + return entry; + } + } + + return nil; +} + +static void CanardUpdateCurrentArchiveContext(NSString *archivePath) { + CanardCurrentArchivePath = [archivePath copy]; + CanardCurrentArchiveFileName = [[archivePath lastPathComponent] copy]; + CanardCurrentArchiveEntry = CanardCatalogEntryForFileName(CanardCurrentArchiveFileName); + CanardLastSavedToken = nil; + + NSLog(@"%@ Archive context file=%@ entryFound=%@", kCanardLogTag, CanardCurrentArchiveFileName, CanardCurrentArchiveEntry ? @"YES" : @"NO"); +} + +// #pragma mark - UI Helpers + +// static UIViewController *CanardTopViewController(UIViewController *viewController) { +// if (!viewController) return nil; - if (viewController.presentedViewController) { - return CanardTopViewController(viewController.presentedViewController); - } - if ([viewController isKindOfClass:[UINavigationController class]]) { - return CanardTopViewController([(UINavigationController *)viewController visibleViewController]); - } - if ([viewController isKindOfClass:[UITabBarController class]]) { - return CanardTopViewController([(UITabBarController *)viewController selectedViewController]); - } - return viewController; -} +// if (viewController.presentedViewController) { +// return CanardTopViewController(viewController.presentedViewController); +// } +// if ([viewController isKindOfClass:[UINavigationController class]]) { +// return CanardTopViewController([(UINavigationController *)viewController visibleViewController]); +// } +// if ([viewController isKindOfClass:[UITabBarController class]]) { +// return CanardTopViewController([(UITabBarController *)viewController selectedViewController]); +// } +// return viewController; +// } -static void CanardPresentShareSheet(NSString *filePath) { - dispatch_async(dispatch_get_main_queue(), ^{ - UIWindow *keyWindow = nil; - for (UIWindow *window in [UIApplication sharedApplication].windows) { - if (window.isKeyWindow) { - keyWindow = window; - break; - } - } +// static void CanardPresentShareSheet(NSString *filePath) { +// dispatch_async(dispatch_get_main_queue(), ^{ +// UIWindow *keyWindow = nil; +// for (UIWindow *window in [UIApplication sharedApplication].windows) { +// if (window.isKeyWindow) { +// keyWindow = window; +// break; +// } +// } - UIViewController *topVC = CanardTopViewController(keyWindow.rootViewController); - if (!topVC) { - NSLog(@"%@ Unable to present share sheet", kCanardLogTag); - return; - } +// UIViewController *topVC = CanardTopViewController(keyWindow.rootViewController); +// if (!topVC) { +// NSLog(@"%@ Unable to present share sheet", kCanardLogTag); +// return; +// } - NSURL *fileURL = [NSURL fileURLWithPath:filePath]; - UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:@[fileURL] applicationActivities:nil]; - shareController.popoverPresentationController.sourceView = topVC.view; - shareController.popoverPresentationController.sourceRect = CGRectMake( - CGRectGetMidX(topVC.view.bounds), - CGRectGetMidY(topVC.view.bounds), - 0, 0 - ); - [topVC presentViewController:shareController animated:YES completion:nil]; - }); -} +// NSURL *fileURL = [NSURL fileURLWithPath:filePath]; +// UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:@[fileURL] applicationActivities:nil]; +// shareController.popoverPresentationController.sourceView = topVC.view; +// shareController.popoverPresentationController.sourceRect = CGRectMake( +// CGRectGetMidX(topVC.view.bounds), +// CGRectGetMidY(topVC.view.bounds), +// 0, 0 +// ); +// [topVC presentViewController:shareController animated:YES completion:nil]; +// }); +// } #pragma mark - File Helpers @@ -80,27 +156,60 @@ static bool CanardSaveData(NSData *data, NSString *password) { [fm createDirectoryAtPath:dumpDir withIntermediateDirectories:YES attributes:nil error:nil]; } - NSString *fileName = [NSString stringWithFormat:@"file_%@.pdf", CanardSanitizedFilename(password)]; - NSString *filePath = [dumpDir stringByAppendingPathComponent:fileName]; - - if ([data writeToFile:filePath atomically:YES]) { - NSLog(@"%@ Saved to %@ (%lu bytes)", kCanardLogTag, filePath, (unsigned long)data.length); - CanardPresentShareSheet(filePath); - return YES; + NSString *docStem = [[CanardCurrentArchiveFileName stringByDeletingPathExtension] copy]; + if (docStem.length == 0) { + docStem = @"unknown_doc"; } - NSLog(@"%@ Failed to save to %@", kCanardLogTag, filePath); - return NO; + NSString *safeDocStem = CanardSanitizedFilename(docStem); + NSString *safePassword = CanardSanitizedFilename(password); + NSString *baseName = [NSString stringWithFormat:@"%@_%@", safeDocStem, safePassword]; + + NSString *pdfPath = [dumpDir stringByAppendingPathComponent:[baseName stringByAppendingString:@".pdf"]]; + NSString *jsonPath = [dumpDir stringByAppendingPathComponent:[baseName stringByAppendingString:@".json"]]; + + if (![data writeToFile:pdfPath atomically:YES]) { + NSLog(@"%@ Failed to save to %@", kCanardLogTag, pdfPath); + return NO; + } + + NSMutableDictionary *payload = [NSMutableDictionary dictionary]; + payload[@"archive_path"] = CanardCurrentArchivePath ?: @""; + payload[@"file_name"] = CanardCurrentArchiveFileName ?: @""; + payload[@"password"] = password ?: @""; + payload[@"saved_at"] = [[NSDate date] description]; + payload[@"domain"] = kCanardCatalogManagerDefaultsDomain; + + if ([CanardCurrentArchiveEntry isKindOfClass:[NSDictionary class]]) { + payload[@"catalog_entry"] = CanardCurrentArchiveEntry; + } + + NSError *jsonError = nil; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload + options:NSJSONWritingPrettyPrinted + error:&jsonError]; + if (jsonData) { + [jsonData writeToFile:jsonPath atomically:YES]; + } else { + NSLog(@"%@ Failed to build metadata JSON: %@", kCanardLogTag, jsonError); + } + + NSLog(@"%@ Saved PDF %@ (%lu bytes)", kCanardLogTag, pdfPath, (unsigned long)data.length); + NSLog(@"%@ Saved metadata %@", kCanardLogTag, jsonPath); + + // Present share sheet for the saved PDF + // CanardPresentShareSheet(pdfPath); + return YES; } #pragma mark - DlyArchiveReader Hooks %hook DlyArchiveReader -- (bool)setUpArchiveError:(id *)error { - NSLog(@"%@ setUpArchiveError: %p", kCanardLogTag, error); - return %orig; -} +// - (bool)setUpArchiveError:(id *)error { +// NSLog(@"%@ setUpArchiveError: %p", kCanardLogTag, error); +// return %orig; +// } - (NSString *)password { NSString *pwd = %orig; @@ -109,20 +218,19 @@ static bool CanardSaveData(NSData *data, NSString *password) { return pwd; } -- (int)getDocumentSize { - int size = %orig; - NSLog(@"%@ getDocumentSize: %d", kCanardLogTag, size); - return size; -} +// - (int)getDocumentSize { +// int size = %orig; +// NSLog(@"%@ getDocumentSize: %d", kCanardLogTag, size); +// return size; +// } - (NSData *)readDataAt:(int)offset withSize:(int)size { - NSLog(@"%@ readDataAt: offset=%d size=%d", kCanardLogTag, offset, size); - NSLog(@"%@ %@", kCanardLogTag, [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]); - NSData *data = %orig; - - if (data.length > 0 && size != CanardLastSavedReadSize) { - CanardLastSavedReadSize = size; + NSString *token = [NSString stringWithFormat:@"%@:%d", CanardCurrentArchiveFileName ?: @"unknown", size]; + if (data.length > 0 && ![CanardLastSavedToken isEqualToString:token]) { + NSLog(@"%@ readDataAt: offset=%d size=%d", kCanardLogTag, offset, size); + // NSLog(@"%@ %@", kCanardLogTag, [[NSThread callStackSymbols] componentsJoinedByString:@"\n"]); + CanardLastSavedToken = token; CanardSaveData(data, CanardArchivePassword); } @@ -137,17 +245,37 @@ static bool CanardSaveData(NSData *data, NSString *password) { - (instancetype)initWithArchivePath:(NSString *)path error:(NSError **)error { NSLog(@"%@ initWithArchivePath: %@ error:%p", kCanardLogTag, path, error); + CanardUpdateCurrentArchiveContext(path); return %orig; } + (instancetype)newWithArchivePath:(NSString *)path error:(NSError **)error { NSLog(@"%@ newWithArchivePath: %@ error:%p", kCanardLogTag, path, error); + CanardUpdateCurrentArchiveContext(path); return %orig; } -- (bool)openArchiveWithType:(NSUInteger)type error:(id *)error { - NSLog(@"%@ openArchiveWithType: %lu error:%p", kCanardLogTag, (unsigned long)type, error); - return %orig; +// - (bool)openArchiveWithType:(NSUInteger)type error:(id *)error { +// NSLog(@"%@ openArchiveWithType: %lu error:%p", kCanardLogTag, (unsigned long)type, error); +// return %orig; +// } + +%end + + +# pragma mark - NSURLSessionConfiguration Hooks to prevent background sessions (not available in simulator) +%hook NSURLSessionConfiguration + ++ (NSURLSessionConfiguration *)backgroundSessionConfigurationWithIdentifier:(NSString *)identifier { + NSLog(@"%@ backgroundSessionConfigurationWithIdentifier:'%@' -> forcing default config", kCanardLogTag, identifier); + return [NSURLSessionConfiguration defaultSessionConfiguration]; } -%end \ No newline at end of file +%end + + +# pragma mark - Apply privacy defaults on tweak load + +%ctor { + CanardApplyForcedDefaults(); +} \ No newline at end of file