|
@@ -34,7 +34,7 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
|
|
|
|
|
|
|
|
|
#define DLog(format, ...) CFShow((__bridge CFStringRef)[NSString stringWithFormat:format, ## __VA_ARGS__]);
|
|
|
-
|
|
|
+#define FM [NSFileManager defaultManager]
|
|
|
/*
|
|
|
|
|
|
fileType, @"fileType",octalPermissions, @"octalPermissions", octalUG, @"octalUG", size, @"size", date, @"date", time, @"time", linkDest, @"linkDest", fullPath, @"fullPath", nil];
|
|
@@ -85,6 +85,233 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
|
|
|
@end
|
|
|
|
|
|
|
|
|
+@interface DebPackageModel : NSObject
|
|
|
+
|
|
|
+@property (nonatomic, copy) NSString *name;
|
|
|
+@property (nonatomic, copy) NSString *package;
|
|
|
+@property (nonatomic, copy) NSString *source;
|
|
|
+@property (nonatomic, copy) NSString *version;
|
|
|
+@property (nonatomic, copy) NSString *priority;
|
|
|
+@property (nonatomic, copy) NSString *essential;
|
|
|
+@property (nonatomic, copy) NSArray *depends;
|
|
|
+@property (nonatomic, copy) NSString *maintainer;
|
|
|
+@property (nonatomic, copy) NSString *packageDescription; //cant be description, that is reservered
|
|
|
+@property (nonatomic, copy) NSString *homepage;
|
|
|
+@property (nonatomic, copy) NSString *author;
|
|
|
+@property (nonatomic, copy) NSString *icon;
|
|
|
+@property (nonatomic, copy) NSString *depiction;
|
|
|
+@property (nonatomic, copy) NSString *preDepends;
|
|
|
+@property (nonatomic, copy) NSString *breaks;
|
|
|
+@property (nonatomic, copy) NSString *status;
|
|
|
+@property (nonatomic, copy) NSString *tag;
|
|
|
+@property (nonatomic, copy) NSString *architecture;
|
|
|
+@property (nonatomic, copy) NSString *section;
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+- (instancetype)initWithRawControlString:(NSString *)controlString;
|
|
|
+
|
|
|
+
|
|
|
+@end
|
|
|
+@implementation DebPackageModel
|
|
|
+
|
|
|
+- (NSString *)description {
|
|
|
+
|
|
|
+ NSString *orig = [super description];
|
|
|
+ return [NSString stringWithFormat:@"%@ = %@ (%@)", orig, self.package, self.version];
|
|
|
+}
|
|
|
+
|
|
|
+- (NSString*) fullDescription {
|
|
|
+
|
|
|
+ NSString *orig = [super description];
|
|
|
+ NSMutableDictionary *details = [NSMutableDictionary new];
|
|
|
+ NSArray *props = [self properties];
|
|
|
+ [props enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
|
+
|
|
|
+ NSString *cv = [self valueForKey:obj];
|
|
|
+ if (cv){
|
|
|
+ details[obj] = cv;
|
|
|
+ }
|
|
|
+
|
|
|
+ }];
|
|
|
+ return [NSString stringWithFormat:@"%@ = %@", orig, details];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
++ (NSDictionary *)dependencyDictionaryFromString:(NSString *)depend
|
|
|
+{
|
|
|
+ NSMutableCharacterSet *whitespaceAndPunctuationSet = [NSMutableCharacterSet characterSetWithCharactersInString:@"()"];
|
|
|
+ [whitespaceAndPunctuationSet formUnionWithCharacterSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
+
|
|
|
+ NSScanner *stringScanner = [[NSScanner alloc] initWithString:depend];
|
|
|
+ stringScanner.charactersToBeSkipped = whitespaceAndPunctuationSet;
|
|
|
+
|
|
|
+ NSString *name = nil;
|
|
|
+ NSInteger i = 0;
|
|
|
+ NSMutableDictionary *predicate = [NSMutableDictionary new];
|
|
|
+ while ([stringScanner scanUpToCharactersFromSet:whitespaceAndPunctuationSet intoString:&name]) {
|
|
|
+ // NSLog(@"%@ pass %li", name, (long)i);
|
|
|
+ switch (i) {
|
|
|
+ case 0 :
|
|
|
+ predicate[@"package"] = name;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ predicate[@"predicate"] = name;
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ predicate[@"requirement"] = name;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ i++;
|
|
|
+ }
|
|
|
+ return predicate;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
++ (NSArray *)dependencyArrayFromString:(NSString *)depends
|
|
|
+{
|
|
|
+ NSMutableArray *cleanArray = [[NSMutableArray alloc] init];
|
|
|
+ NSArray *dependsArray = [depends componentsSeparatedByString:@","];
|
|
|
+ for (id depend in dependsArray)
|
|
|
+ {
|
|
|
+ NSArray *spaceDelimitedArray = [depend componentsSeparatedByString:@" "];
|
|
|
+ NSString *isolatedDependency = [[spaceDelimitedArray objectAtIndex:0] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
+ if ([isolatedDependency length] == 0)
|
|
|
+ isolatedDependency = [[spaceDelimitedArray objectAtIndex:1] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
|
+
|
|
|
+ NSDictionary *dependDict = [self dependencyDictionaryFromString:depend];
|
|
|
+ //DLog(@"depend dict: %@", dependDict);
|
|
|
+ [cleanArray addObject:dependDict];
|
|
|
+ }
|
|
|
+
|
|
|
+ return cleanArray;
|
|
|
+}
|
|
|
+
|
|
|
++ (NSDictionary *)JSONKeyPathsByPropertyKey {
|
|
|
+ return @{
|
|
|
+ @"name": @"Name",
|
|
|
+ @"package": @"Package",
|
|
|
+ @"source": @"Source",
|
|
|
+ @"version": @"Version",
|
|
|
+ @"priority": @"Priority",
|
|
|
+ @"essential": @"Essential",
|
|
|
+ @"depends": @"Depends",
|
|
|
+ @"maintainer": @"Maintainer",
|
|
|
+ @"packageDescription": @"Description",
|
|
|
+ @"homepage": @"Homepage",
|
|
|
+ @"icon": @"Icon",
|
|
|
+ @"author": @"Author",
|
|
|
+ @"preDepends": @"Pre-Depends",
|
|
|
+ @"breaks": @"Breaks",
|
|
|
+ @"depiction": @"Depiction",
|
|
|
+ @"tag": @"Tag",
|
|
|
+ @"architecture": @"Architecture",
|
|
|
+ @"section": @"Section",
|
|
|
+ @"osMax": @"osMax",
|
|
|
+ @"osMin": @"osMin",
|
|
|
+ @"banner": @"Banner",
|
|
|
+ @"topShelfImage": @"TopShelfImage"
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+- (instancetype)initWithRawControlString:(NSString *)controlString
|
|
|
+{
|
|
|
+ NSArray *packageArray = [controlString componentsSeparatedByString:@"\n"];
|
|
|
+ NSMutableDictionary *currentPackage = [[NSMutableDictionary alloc] init];
|
|
|
+ for (id currentLine in packageArray)
|
|
|
+ {
|
|
|
+ NSArray *itemArray = [currentLine componentsSeparatedByString:@": "];
|
|
|
+ if ([itemArray count] >= 2)
|
|
|
+ {
|
|
|
+ NSString *key = [itemArray objectAtIndex:0];
|
|
|
+ NSString *object = [itemArray objectAtIndex:1];
|
|
|
+
|
|
|
+ if ([key isEqualToString:@"Depends"]) //process the array
|
|
|
+ {
|
|
|
+ NSArray *dependsObject = [DebPackageModel dependencyArrayFromString:object];
|
|
|
+
|
|
|
+ [currentPackage setObject:dependsObject forKey:key];
|
|
|
+
|
|
|
+ } else { //every other key, even if it has an array is treated as a string
|
|
|
+
|
|
|
+ [currentPackage setObject:object forKey:key];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if ([[currentPackage allKeys] count] > 4)
|
|
|
+ {
|
|
|
+ self = [super init];
|
|
|
+ [self mapDictionaryToProperties:currentPackage];
|
|
|
+ return self;
|
|
|
+ }
|
|
|
+ return nil;
|
|
|
+
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+- (void)mapDictionaryToProperties:(NSDictionary *)theProps {
|
|
|
+
|
|
|
+ NSArray *ourProps = [self properties];
|
|
|
+ NSArray *allKeys = [theProps allKeys];
|
|
|
+ NSDictionary *mappedKeys = [DebPackageModel JSONKeyPathsByPropertyKey];
|
|
|
+ [ourProps enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
|
|
|
+
|
|
|
+ NSString *mappedProp = mappedKeys[obj];
|
|
|
+
|
|
|
+ //DLog(@"allkeys: %@", allKeys);
|
|
|
+
|
|
|
+ if ([allKeys containsObject:mappedProp]){
|
|
|
+ if ([self respondsToSelector:NSSelectorFromString(obj)]){ //redudant
|
|
|
+
|
|
|
+ id value = theProps[mappedProp];
|
|
|
+ //DLog(@"setting value: %@ for key: %@ from mapped key: %@", value, obj, mappedProp);
|
|
|
+ [self setValue:value forKey:obj];
|
|
|
+
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+
|
|
|
+ //DLog(@"%@ doesnt respond to %@", self, mappedProp);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ }];
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+@interface NSString (Additions)
|
|
|
+
|
|
|
+- (void)writeToFileWithoutAttributes:(NSString *)theFile;
|
|
|
+
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+@implementation NSString (Additions)
|
|
|
+
|
|
|
+- (void)writeToFileWithoutAttributes:(NSString *)theFile {
|
|
|
+
|
|
|
+ if ([FM fileExistsAtPath:theFile]){
|
|
|
+
|
|
|
+ DLog(@"overwriting file: %@", theFile);
|
|
|
+ }
|
|
|
+ FILE *fd = fopen([theFile UTF8String], "w+");
|
|
|
+ const char *text = self.UTF8String;
|
|
|
+ fwrite(text, strlen(text) + 1, 1, fd);
|
|
|
+ fclose(fd);
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+@end
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
@interface PackageFile: NSObject
|
|
|
|
|
@@ -205,6 +432,7 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
|
|
|
@property (nonatomic, strong) NSArray <PackageFile *> *files;
|
|
|
@property (nonatomic, strong) NSArray *controlFiles;
|
|
|
@property (nonatomic, strong) NSString *packageName;
|
|
|
+@property (nonatomic, strong) NSString *version;
|
|
|
|
|
|
- (ErrorReturn *)errorReturnForBootstrap:(NSString *)bootstrapPath;
|
|
|
- (NSString *)listfile;
|
|
@@ -304,7 +532,37 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
|
|
|
|
|
|
@implementation HelperClass
|
|
|
|
|
|
-
|
|
|
++ (NSArray <DebPackageModel*>*)statusInstalledPackagesFromFile:(NSString *)statusFile
|
|
|
+{
|
|
|
+
|
|
|
+ if (![FM fileExistsAtPath:statusFile]) {
|
|
|
+ return nil;
|
|
|
+ }
|
|
|
+ NSString *packageString = [NSString stringWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
|
|
|
+ NSArray *lineArray = [packageString componentsSeparatedByString:@"\n\n"];
|
|
|
+ //DDLogInfo(@"lineArray: %@", lineArray);
|
|
|
+ NSMutableArray *mutableList = [[NSMutableArray alloc] init];
|
|
|
+ //NSMutableDictionary *mutableDict = [[NSMutableDictionary alloc] init];
|
|
|
+ for (id currentItem in lineArray)
|
|
|
+ {
|
|
|
+
|
|
|
+ DebPackageModel *debModel = [[DebPackageModel alloc] initWithRawControlString:currentItem];
|
|
|
+ if (debModel != nil)
|
|
|
+ [mutableList addObject:debModel];
|
|
|
+ }
|
|
|
+
|
|
|
+ NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES
|
|
|
+ selector:@selector(localizedCaseInsensitiveCompare:)];
|
|
|
+ NSSortDescriptor *packageDescriptor = [[NSSortDescriptor alloc] initWithKey:@"package" ascending:YES
|
|
|
+ selector:@selector(localizedCaseInsensitiveCompare:)];
|
|
|
+ NSArray *descriptors = [NSArray arrayWithObjects:nameDescriptor, packageDescriptor, nil];
|
|
|
+ NSArray *sortedArray = [mutableList sortedArrayUsingDescriptors:descriptors];
|
|
|
+
|
|
|
+
|
|
|
+ mutableList = nil;
|
|
|
+
|
|
|
+ return sortedArray;
|
|
|
+}
|
|
|
|
|
|
+ (BOOL)shouldContinueWithError:(NSString *)errorMessage {
|
|
|
|
|
@@ -513,9 +771,14 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
|
|
|
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ (Package *)packageForDeb:(NSString *)debFile {
|
|
|
|
|
|
NSString *packageName = [[self returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -f %@ Package", debFile]] componentsJoinedByString:@"\n"];
|
|
|
+ NSString *packageVersion = [[self returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -f %@ Version", debFile]] componentsJoinedByString:@"\n"];
|
|
|
NSArray <PackageFile *> *fileList = [self returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -c %@", debFile]];
|
|
|
|
|
|
__block NSMutableArray *finalArray = [NSMutableArray new];
|
|
@@ -533,6 +796,7 @@ typedef NS_ENUM(NSInteger, BSPackageFileType)
|
|
|
Package *pkg = [Package new];
|
|
|
pkg.files = finalArray;
|
|
|
pkg.packageName = packageName;
|
|
|
+ pkg.version = packageVersion;
|
|
|
return pkg;
|
|
|
|
|
|
}
|
|
@@ -687,10 +951,47 @@ int main(int argc, const char * argv[]) {
|
|
|
}
|
|
|
|
|
|
if (debFile && bootstrapPath) {
|
|
|
+
|
|
|
+ NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
|
|
|
+ NSArray *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
|
|
|
+
|
|
|
+ //DLog(@"installedPackages: %@", installedPackages);
|
|
|
|
|
|
DLog(@"\n\nProcessing file: %@\n", debFile);
|
|
|
Package *output = [HelperClass packageForDeb:debFile];
|
|
|
|
|
|
+ DLog(@"\nFound package: '%@' at version: '%@'...\n", output.packageName, output.version );
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ DebPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", output.packageName]] lastObject];
|
|
|
+
|
|
|
+ if (model) {
|
|
|
+
|
|
|
+ DLog(@"\n [WARNING] This package has already been installed! %@", model);
|
|
|
+ NSComparisonResult result = [output.version compare:model.version options:NSNumericSearch];
|
|
|
+ DLog(@"\n [WARNING] Comparing version numbers...");
|
|
|
+ switch (result) {
|
|
|
+ case NSOrderedSame:
|
|
|
+ DLog(@"\n [WARNING] %@ and %@ match!", output.version, model.version);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NSOrderedAscending:
|
|
|
+ DLog(@"\n [WARNING] Package version %@ is less than installed version %@!", output.version, model.version);
|
|
|
+ break;
|
|
|
+
|
|
|
+ case NSOrderedDescending:
|
|
|
+ DLog(@"\n [WARNING] Package version %@ is greater than installed version %@!", output.version, model.version);
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
ErrorReturn *safePackage = [output errorReturnForBootstrap:bootstrapPath];
|
|
|
|
|
|
if (safePackage.returnStatus != 0) { //zero is success
|
|
@@ -718,17 +1019,35 @@ int main(int argc, const char * argv[]) {
|
|
|
//DLog(@"%@", output);
|
|
|
//DLog(@"list: %@", output.listfile);
|
|
|
NSFileManager *man = [NSFileManager defaultManager];
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
NSString *tmpPath = [pwd stringByAppendingPathComponent:output.packageName];
|
|
|
NSString *debian = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
|
|
|
[man createDirectoryAtPath:tmpPath withIntermediateDirectories:TRUE attributes:nil error:nil];
|
|
|
+ DLog(@"\nExtracting deb for processing...\n");
|
|
|
[HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -x %@ %@", debFile, tmpPath]];
|
|
|
- NSString *listFile = [pwd stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.list", output.packageName]];
|
|
|
- NSString *md5s = [pwd stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.md5sums", output.packageName]];
|
|
|
+
|
|
|
+ NSString *bootstrapInfoPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
|
|
|
+
|
|
|
+ NSString *listFile = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.list", output.packageName]];
|
|
|
+ NSString *md5s = [bootstrapInfoPath stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.md5sums", output.packageName]];
|
|
|
+
|
|
|
+ DLog(@"\nCreating list file '%@'...\n", listFile);
|
|
|
+
|
|
|
[output.listfile writeToFile:listFile atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
|
|
|
+
|
|
|
+ DLog(@"\nGenerating md5sums...\n");
|
|
|
NSString *runString = [NSString stringWithFormat:@"/usr/bin/find %@ -type f -not -path \"*.DS_Store*\" -exec /sbin/md5 -r {} \\; | /usr/bin/awk '{ print $1 \" \" $2 }' | /usr/bin/sed \"s|%@||g\"", tmpPath, tmpPath];
|
|
|
NSString *outputs = [[HelperClass returnForProcess:runString] componentsJoinedByString:@"\n"];
|
|
|
+
|
|
|
+ DLog(@"\nCreating md5sum file '%@'...\n", listFile);
|
|
|
+
|
|
|
[outputs writeToFile:md5s atomically:TRUE encoding:NSUTF8StringEncoding error:nil];
|
|
|
+
|
|
|
[man createDirectoryAtPath:debian withIntermediateDirectories:TRUE attributes:nil error:nil];
|
|
|
+
|
|
|
[HelperClass returnForProcess:[NSString stringWithFormat:@"/usr/local/bin/dpkg -e %@ %@", debFile, debian]];
|
|
|
//NSString *nextPath = [tmpPath stringByAppendingPathComponent:@"DEBIAN"];
|
|
|
|
|
@@ -750,8 +1069,10 @@ int main(int argc, const char * argv[]) {
|
|
|
|
|
|
|
|
|
[controlFile replaceOccurrencesOfString:@"iphoneos-arm" withString:@"appletvos-arm64" options:NSLiteralSearch range:NSMakeRange(0, [controlFile length])];
|
|
|
+
|
|
|
DLog(@"control file: %@", controlFile);
|
|
|
|
|
|
+
|
|
|
NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
|
|
|
|
|
|
NSMutableString *statusContents = [[NSMutableString alloc] initWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
|