Skip to content

Instantly share code, notes, and snippets.

@ttwj
Created January 29, 2016 07:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ttwj/e407c957b24d00ea434b to your computer and use it in GitHub Desktop.
Save ttwj/e407c957b24d00ea434b to your computer and use it in GitHub Desktop.
//
// ApplicationsManager.m
// Clutch
//
// Created by Anton Titkov on 09.02.15.
//
//
#define applistCachePath @"applist-cache.plist"
#define dumpedAppPath @"/etc/dumped.clutch"
#import <dlfcn.h>
#import "ApplicationsManager.h"
#import "FBApplicationInfo.h"
#import "LSApplicationProxy.h"
#import "LSApplicationWorkspace.h"
typedef NSDictionary* (*MobileInstallationLookup)(NSDictionary *options);
@interface ApplicationsManager ()
{
void * _MIHandle;
MobileInstallationLookup _mobileInstallationLookup;
NSMutableArray* _cachedApps;
}
@end
@implementation ApplicationsManager
+ (instancetype)sharedInstance
{
static dispatch_once_t pred;
static id shared = nil;
dispatch_once(&pred, ^{
shared = [self new];
});
return shared;
}
- (instancetype)init
{
self = [super init];
if (self) {
_MIHandle = dlopen("/System/Library/PrivateFrameworks/MobileInstallation.framework/MobileInstallation", RTLD_NOW);
_mobileInstallationLookup = NULL;
if (_MIHandle)
_mobileInstallationLookup = dlsym(_MIHandle,"MobileInstallationLookup");
}
if ([[NSFileManager defaultManager] fileExistsAtPath:applistCachePath]) {
_cachedApps = [[NSMutableArray alloc] initWithContentsOfFile:applistCachePath];
}
else {
_cachedApps = [NSMutableArray new];
}
return self;
}
- (NSDictionary *)_allApplications
{
_cachedApps = [NSMutableArray new];
NSMutableDictionary *returnValue = [NSMutableDictionary new];
NSDictionary* options = @{@"ApplicationType":@"User",
@"ReturnAttributes":@[@"CFBundleShortVersionString",
@"CFBundleVersion",
@"Path",
@"CFBundleDisplayName",
@"CFBundleExecutable",
@"MinimumOSVersion"]};
if (_mobileInstallationLookup) {
NSLog(@"using MSLookup");
NSDictionary *installedApps;
installedApps = _mobileInstallationLookup(options);
for (NSString *bundleID in [installedApps allKeys])
{
NSDictionary *appI=installedApps[bundleID];
NSURL *bundleURL = [NSURL fileURLWithPath:appI[@"Path"]];
NSString *scinfo=[bundleURL.path stringByAppendingPathComponent:@"SC_Info"];
BOOL isDirectory;
BOOL purchased = [[NSFileManager defaultManager]fileExistsAtPath:scinfo isDirectory:&isDirectory];
if (purchased && isDirectory) {
NSString* name = appI[@"CFBundleDisplayName"];
if (name == nil) {
name = appI[@"CFBundleExecutable"];
}
NSLog(@"initializing %@ %@ %@ %@", bundleURL.URLByDeletingLastPathComponent, bundleURL, proxy.bundleURL, name, bundleID);
NSDictionary* bundleInfo = @{@"BundleContainer":bundleURL.URLByDeletingLastPathComponent,
@"BundleURL":bundleURL,
@"DisplayName": name,
@"BundleIdentifier": bundleID};
Application *app =[[Application alloc]initWithBundleInfo:bundleInfo];
[self cacheBundle:bundleInfo];
returnValue[bundleID] = app;
}
}
}else
{
NSLog(@"using LSAppWorkSpace");
LSApplicationWorkspace* applicationWorkspace = [LSApplicationWorkspace defaultWorkspace];
NSArray *proxies = [applicationWorkspace allApplications];
for (FBApplicationInfo * proxy in proxies) {
NSString *appType = [proxy performSelector:@selector(applicationType)];
if ([appType isEqualToString:@"User"] && proxy.bundleContainerURL && proxy.bundleURL) {
NSString *scinfo=[proxy.bundleURL.path stringByAppendingPathComponent:@"SC_Info"];
BOOL isDirectory;
BOOL purchased = [[NSFileManager defaultManager]fileExistsAtPath:scinfo isDirectory:&isDirectory];
if (purchased && isDirectory) {
NSLog(@"initializing %@ %@ %@ %@", proxy.bundleContainerURL, proxy.bundleURL, ((LSApplicationProxy*) proxy).itemName, proxy.bundleIdentifier);
NSDictionary* bundleInfo = @{@"BundleContainer":proxy.bundleContainerURL,
@"BundleURL":proxy.bundleURL,
@"DisplayName": ((LSApplicationProxy*) proxy).itemName,
@"BundleIdentifier": proxy.bundleIdentifier};
Application *app =[[Application alloc]initWithBundleInfo:bundleInfo];
returnValue[proxy.bundleIdentifier] = app;
[self cacheBundle:bundleInfo];
}
}
}
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
dispatch_async(queue, ^{
[_cachedApps writeToFile:applistCachePath atomically:YES];
});
return [returnValue copy];
}
- (NSDictionary *)installedApps
{
return [self _allApplications];
}
-(NSDictionary*)_allCachedApplications {
if ([_cachedApps count] < 1) {
return [self _allApplications];
}
NSMutableDictionary* returnValue = [NSMutableDictionary new];
for (NSDictionary* bundleInfo in _cachedApps) {
Application *app =[[Application alloc]initWithBundleInfo:bundleInfo];
returnValue[bundleInfo[@"BundleIdentifier"]] = app;
}
return returnValue;
}
-(void)cacheBundle:(NSDictionary*) bundle {
[_cachedApps addObject:bundle];
}
- (NSArray *)dumpedApps
{
NSString *dumpedPath = @""; //[NSString stringWithFormat:@"%@/", [[Preferences sharedInstance] ipaDirectory]];
NSArray *array=[[NSArray alloc]initWithArray:[[NSFileManager defaultManager] contentsOfDirectoryAtPath:dumpedPath error:nil]];
NSMutableArray *paths=[NSMutableArray new];
for (int i=0; i<array.count; i++)
{
if (![[array[i] pathExtension] caseInsensitiveCompare:@"ipa"])
{
[paths addObject:array[i]];
}
}
return paths;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment