#define ARM

#include <objc/runtime.h>
#include <libgen.h>
#include <dlfcn.h>
#include <CoreFoundation/CoreFoundation.h>
#ifdef ARM
#include <Foundation/Foundation.h> // NSObject
#endif

@interface LSApplicationWorkspaceRemoteObserver : NSObject
-(id)localObservers;
@end


@interface LSApplicationWorkspace : NSObject
+ (id) defaultWorkspace;
@property (readonly) LSApplicationWorkspaceRemoteObserver * remoteObserver;
- (BOOL) _LSPrivateSyncWithMobileInstallation;
@end

@interface PBAppDepot : NSObject
+ (id)sharedInstance;
@property(retain, nonatomic) NSMutableDictionary *internalAppState;
- (id)_addAppStateForIdentifier:(id)arg1;
- (void)_save;
- (void)_setNeedsNotifyAppStateDidChange;
@end



//#import "KBHelperClass.h"

//
// LSDTrip: A simple tool to demonstrate LaunchServices in OS X *and* iOS
// -------
//
// Jonathan Levin, http://NeWOSXBook.com  - @Morpheus______/@TechnoloGeeks
//
// License: free, but if you GitHub it or mod it, at least leave a mention.
//          And maybe get the book when it's finally out :-)
//
// Compile: gcc ls.m -o lsdtrip -lobjc -framework Foundation
//  or
// gcc-iphone -DARM ls.m -o lsdtrip -lobjc -framework Foundation -framework MobileCoreServices
//
// (that "MobileCoreServices" is required for the dlopen(3) to work)
//
//
// To run:
// Usage: ls [apps|plugins|publicurls|privateurls] [-v]
//            whohas _url_
//	      types
//            dump <<-- Try this
//
// Explanation:
//
//   Shows what are (IMHO) the most useful of the LaunchServices APIs, as exported by
//   [Mobile]CoreServices. In iOS most of the good stuff isn't exported (t), instead wrapped
//   by LSApplicationWorkSpace, LSApplicationProxy, and friends. Even though the straight LS
//   and _LS calls are much nicer, I chose to use objective-C here in order to maintain
//   write-once-run-everywhere, and demonstrate yet again just how OS X and iOS really are
//   so similar.
//
// How this works:
//
//   Over rather nice XPC to lsd (10.11/9.0) and friends. Full explanation in MOXiI 2.
//  (Chapter 5, "Promenade, A Tour of the OS X and iOS Frameworks", to be exact, where
//  many more "private" frameworks are exposed). But since I'm running behind with it,
//  I thought people would appreciate a hands-on preview :-) This is just one of the many
//  open source examples I have planned.
//
//   PLEASE BE PATIENT. This should give you an idea of why MOXiI is delayed. At least I try
//   to keep everyone updated and drop examples along the way. And there's plenty more where
//   this came from. Just wait.
//
//
// Improvements:
//
//   - Can be made into pure C (i.e. not objective-C, .m), but that would make
//     the already ugly [] syntax even worse..
//
//   - There's a whole undocumented side to LaunchServices (even as an allegedly
//     "public" framework that it is) one can add here. And I left the other
//     methods out in an #if 0 block, for future use.
//
//   - The UUIDs aren't actually CFUUIDs. They're some $%#$%#$ NS...UUID, which
//     isn't compatible, so the UUIDs come with some 0s at the end. Meh.
//

// Ok. Let's begin:

// My own prototypes and globals:

CFStringRef dumpApp (NSObject *AppRef, int Verbose) ;

NSObject * workspace; // Intentionally void * so as to NOT involve @interface files

// OS X and iOS APIs are virtually identical, but the framework name is different
#ifdef ARM
#define  CORE_SERVICE_FRAMEWORK  "/System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices"
#else
#define  CORE_SERVICE_FRAMEWORK  "/System/Library/Frameworks/CoreServices.framework/CoreServices"
#endif


CFStringRef
serializeCFArrayToCFString (CFArrayRef Array, CFStringRef Delimiter)
{
    
    if (!Array) { return (CFSTR("(null)"));}
    CFMutableStringRef returned = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc,
                                                        4096); //CFIndex maxLength);
    
    
    int len = CFArrayGetCount(Array);
    int i = 0;
    for (i = 0; i < len ; i++)
        
    {
        CFTypeRef val = (CFTypeRef) CFArrayGetValueAtIndex(Array, i);
        
        if (i > 0) CFStringAppend(returned, Delimiter);
        
        // UGLY, I know. But PoC, people. PoC
        if (CFGetTypeID(val) == CFStringGetTypeID()) CFStringAppend(returned, val);
        
        else
            if (CFGetTypeID(val) == CFUUIDGetTypeID()){
                CFStringRef UUID = CFUUIDCreateString(kCFAllocatorDefault, val);
                CFStringAppend(returned, UUID);
                
            }
            else {
                CFStringAppend(returned, dumpApp (val, 0));
            };
    }
    
    
    return (returned);
    
    
} // serializeCFArrayToCFstring


CFStringRef
dumpApp (NSObject *AppRef, int Verbose)
{
    // App is an LSApplicationProxy object
    
    CFStringRef appID = (CFStringRef) [AppRef performSelector:@selector( applicationIdentifier)];
    CFStringRef appName =  (CFStringRef)[AppRef performSelector:@selector(localizedName)];
    
    if (!appName) { appName = CFSTR("Not Set");}
    CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc,
                                                   4096); //CFIndex maxLength);
    CFStringAppendFormat(out, // CFMutableStringRef theString,
                         NULL,   // CFDictionaryRef formatOptions,
                         CFSTR("\t%@ (%@)\n"),
                         appName, appID);
    
    
#if 0
    // Can also use objective-C to enumerate ivars..
    unsigned int ivarCount;
    Ivar *ivars = class_copyIvarList([AppRef class], &ivarCount);
    
    int i = 0;
    for (i = 0; i < ivarCount; i++)
    {
        fprintf(stderr,"\t%s: \n" , ivar_getName(ivars[i]));
        // etc.
    }
#endif
    if (Verbose)
    {
        // Dump more
        
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tExecutable: %@\n"),
                             [AppRef performSelector:@selector(bundleExecutable)]);
        
#ifdef ARM
        // Set on iOS. Can also use versionIdentifier here, but that requires working back from the
        // number to a version string (which LaunchServices lets you do with
        // LSVersionNumberCopyStringRepresentation/LSVersionNumberGetXXX()
        // But why bother when you have a short version string..
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tVersion: %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector(shortVersionString)]);
        
#endif
        
        // This is apparently unset..
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tVendor Name: %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector(vendorName)]);
        
        
        
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tMach-O UUIDs: %@\n"),
                             serializeCFArrayToCFString((CFArrayRef)[AppRef performSelector:@selector(machOUUIDs)], CFSTR(",")));
        
        
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tDisk Usage (Static): %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector(staticDiskUsage)]);
        
        
#if 0
        // This apparently doesn't work in 9.2 anymore. Not sure about this..
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tDisk Usage (Dynamic): %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector(dynamicDiskUsage)]);
        
#endif
        
        CFArrayRef UIBackgroundModes = (CFArrayRef) [AppRef performSelector: @selector(UIBackgroundModes)];
        
        
        // This is a CFArray
        if (!CFArrayGetCount(UIBackgroundModes)) {
            CFStringAppend (out,CFSTR("\t\tno BackgroundModes"));
        }
        
        else
            CFStringAppendFormat(out, // CFMutableStringRef theString,
                                 NULL,   // CFDictionaryRef formatOptions,
                                 CFSTR("\t\t   BackgroundModes: %@"),
                                 serializeCFArrayToCFString((CFArrayRef)UIBackgroundModes, CFSTR(",")));
        
        CFStringAppend(out, CFSTR("\n"));
        
#ifdef ARM
        // Only on iOS
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tApplication DSID: %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector(applicationDSID)]);
        
        
        
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tPurchaser DSID: %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector(purchaserDSID)]);
        
        
        
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tDownloader DSID: %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector(downloaderDSID)]);
#endif
        
        
#if 0
        uint64_t  modTime = (uint64_t)([AppRef performSelector:@selector( bundleModTime)]);
        fprintf(stderr, "\t\tBundle Mod Time: %llu\n", modTime);
#endif
        
        
        int  cont = (int)([AppRef performSelector:@selector( isContainerized)]);
        int  restricted  = (int)([AppRef performSelector:@selector( isRestricted)]);
        
        CFStringAppendFormat(out,
                             NULL,
                             CFSTR("\t\tContainerized: %@\n\t\tRestricted: %@\n"),
                             (cont ? CFSTR("YES (q.v. App-Store Receipt URL for container)") : CFSTR("NO")),
                             (restricted ? CFSTR("YES") : CFSTR("NO")));
        
        
        
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tApp Store Receipt URL: %@\n"),
                             (CFStringRef)[AppRef performSelector:@selector( appStoreReceiptURL)]);
        
        
        // These are from LSBundleProxy, which is the parent of LSApplicationProxy
        CFStringAppendFormat(out, // CFMutableStringRef theString,
                             NULL,   // CFDictionaryRef formatOptions,
                             CFSTR("\t\tContainer URL: %@\n"),
                             [AppRef performSelector:@selector(containerURL)]);
        
        CFDictionaryRef entitlements = (CFDictionaryRef) [AppRef performSelector:@selector(entitlements)];
        
        if (entitlements &&  CFDictionaryGetCount(entitlements) )
        {
            
            CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,
                                                        (CFPropertyListRef)entitlements);
            
            CFStringRef xmlAsString = CFStringCreateFromExternalRepresentation(NULL, xml, kCFStringEncodingUTF8);
            
            if (xmlAsString) {
                CFStringAppendFormat(out, // CFMutableStringRef theString,
                                     NULL,   // CFDictionaryRef formatOptions,
                                     CFSTR("\t\tEntitlements:\n-----\n %@\n-----\n"),
                                     xmlAsString);
                
            }
            else { CFStringAppend (out, CFSTR("\t\tEntitlements: Internal error\n"));}
            
            
        } // entitlements
        else
            CFStringAppend(out,CFSTR("\t\tEntitlements: None\n"));
        
    } // Verbose
    
    return (out);
}


/*
 *
 Soooo much more, courtesy of JTool:
 
 -[LSApplicationProxy applicationIdentifier]:
 -[LSApplicationProxy roleIdentifier]:
 -[LSApplicationProxy bundleModTime]:
 -[LSApplicationProxy registeredDate]:
 -[LSApplicationProxy deviceFamily]:
 -[LSApplicationProxy minimumSystemVersion]:
 -[LSApplicationProxy sdkVersion]:
 -[LSApplicationProxy applicationType]:
 -[LSApplicationProxy itemName]:
 -[LSApplicationProxy sourceAppIdentifier]:
 -[LSApplicationProxy companionApplicationIdentifier]:
 -[LSApplicationProxy applicationVariant]:
 -[LSApplicationProxy storeCohortMetadata]:
 -[LSApplicationProxy shortVersionString]:
 -[LSApplicationProxy preferredArchitecture]:
 -[LSApplicationProxy familyID]:
 -[LSApplicationProxy groupContainers]:
 -[LSApplicationProxy directionsModes]:
 -[LSApplicationProxy UIBackgroundModes]:
 -[LSApplicationProxy audioComponents]:
 -[LSApplicationProxy externalAccessoryProtocols]:
 -[LSApplicationProxy VPNPlugins]:
 -[LSApplicationProxy plugInKitPlugins]:
 -[LSApplicationProxy appTags]:
 -[LSApplicationProxy requiredDeviceCapabilities]:
 -[LSApplicationProxy deviceIdentifierForVendor]:
 -[LSApplicationProxy ODRDiskUsage]:
 -[LSApplicationProxy storeFront]:
 -[LSApplicationProxy externalVersionIdentifier]:
 -[LSApplicationProxy betaExternalVersionIdentifier]:
 -[LSApplicationProxy appStoreReceiptURL]:
 -[LSApplicationProxy installProgress]:
 -[LSApplicationProxy installProgressSync]:
 -[LSApplicationProxy resourcesDirectoryURL]:
 -[LSApplicationProxy privateDocumentIconNames]:
 -[LSApplicationProxy setPrivateDocumentIconNames:]:
 -[LSApplicationProxy privateIconsDictionary]:
 -[LSApplicationProxy privateDocumentIconAllowOverride]:
 -[LSApplicationProxy setPrivateDocumentIconAllowOverride:]:
 -[LSApplicationProxy iconDataForVariant:]:
 -[LSApplicationProxy privateDocumentTypeOwner]:
 -[LSApplicationProxy setPrivateDocumentTypeOwner:]:
 -[LSApplicationProxy localizedName]:
 -[LSApplicationProxy localizedShortName]:
 -[LSApplicationProxy localizedNameForContext:]:
 -[LSApplicationProxy iconIsPrerendered]:
 -[LSApplicationProxy fileSharingEnabled]:
 -[LSApplicationProxy profileValidated]:
 -[LSApplicationProxy isAdHocCodeSigned]:
 -[LSApplicationProxy isPlaceholder]:
 -[LSApplicationProxy isAppUpdate]:
 -[LSApplicationProxy isNewsstandApp]:
 -[LSApplicationProxy isRestricted]:
 -[LSApplicationProxy supportsAudiobooks]:
 -[LSApplicationProxy supportsExternallyPlayableContent]:
 -[LSApplicationProxy supportsOpenInPlace]:
 -[LSApplicationProxy hasSettingsBundle]:
 -[LSApplicationProxy isInstalled]:
 -[LSApplicationProxy isWhitelisted]:
 -[LSApplicationProxy isBetaApp]:
 -[LSApplicationProxy isPurchasedReDownload]:
 -[LSApplicationProxy isWatchKitApp]:
 -[LSApplicationProxy hasMIDBasedSINF]:
 -[LSApplicationProxy missingRequiredSINF]:
 -[LSApplicationProxy isEqual:]:
 -[LSApplicationProxy hash]:
 -[LSApplicationProxy description]:
 -[LSApplicationProxy iconStyleDomain]:
 -[LSApplicationProxy userActivityStringForAdvertisementData:]:
 -[LSApplicationProxy populateNotificationData]:
 -[LSApplicationProxy itemID]:
 -[LSApplicationProxy installType]:
 -[LSApplicationProxy originalInstallType]:
 -[LSApplicationProxy groupIdentifiers]:
 -[LSApplicationProxy teamID]:
 -[LSApplicationProxy isContainerized]:
 */


void
dumpURL (CFStringRef URL, int Verbose)
{
    
    CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc,
                                                   4096); //CFIndex maxLength);
    
    CFStringAppend(out, URL);
    if (Verbose) {
        CFArrayRef appsForURL = (CFArrayRef) [workspace performSelector:@selector(applicationsAvailableForHandlingURLScheme:) withObject:(id)URL];
        
        // This is a CFArray
        if (CFGetTypeID(appsForURL) != CFArrayGetTypeID())
        {
            fprintf(stderr, "Was expecting a CFArray of Apps!\n");
            exit(2);
        }
        
        if (!CFArrayGetCount (appsForURL)) CFStringAppend(out,CFSTR(" - Not claimed by anyone"));
        else {
            
            CFStringRef apps = serializeCFArrayToCFString (appsForURL, CFSTR("\n\t\t\t"));
            
            CFStringAppend (out, apps);
            CFRelease (apps);
        }
    }
    
    CFShow(out);
    CFRelease(out);
}
void
dumpPlugin (NSObject *PluginRef, int Verbose)
{
    
    CFStringRef pluginName = (CFStringRef) [PluginRef performSelector:@selector( localizedName)];
    CFStringRef pluginID = (CFStringRef) [PluginRef performSelector:@selector( pluginIdentifier)];
    CFUUIDRef pluginUUID =  (CFUUIDRef)[PluginRef performSelector:@selector(pluginUUID)];
    
    CFStringRef out = CFStringCreateWithFormat(kCFAllocatorDefault,
                                               NULL, CFSTR("\t%@ (%@) - %@"),
                                               pluginName,pluginID, CFUUIDCreateString(kCFAllocatorDefault, pluginUUID));
    
    CFShow(out);
    
    
} // end Dump

int
main (int argc, char **argv)
{
    
    int verbose = 0;
    
    // So - why dup? because CFShow(), which I use extensively, write to stderr.
    // And I really CANNOT stand the whole CFStringToCstr $%#$%#$ just to get a stupid
    // printout! So instead, I save stderr, and then reopen stderr to stdout. This
    // way, the output can be grep(1)-ed easily.
    
    dup2(2,3);
    dup2(1,2);
    
    
    if (argc < 2)
    {
        
        fprintf(stderr, "Usage: %s [apps|plugins|publicurls|privateurls] [-v]\n", basename(argv[0]));
        fprintf(stderr, "                app _bundle_id_ (implies -v for this app)\n");
#ifdef ARM
        fprintf(stderr, "                launch _bundle_id_\n");
#endif
        fprintf(stderr, "                whohas _url_ \n");
        fprintf(stderr, "		 types\n");
        fprintf(stderr, "                dump\n");
        exit(0);
    }
    
    if (argv[2] && strcmp(argv[2], "-v")==0) verbose++;
    
    // Getting the LS* classes and functions we need here:
    // ---------------------------------------------------
    Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
    if (!LSApplicationWorkspace_class) {fprintf(stderr,"Unable to get Workspace class\n"); exit(1);}
    workspace = [LSApplicationWorkspace_class performSelector:@selector (defaultWorkspace)];
    if (!workspace) {fprintf(stderr,"Unable to get Workspace\n"); exit(2);}
    
    NSLog(@"workspace: %@", workspace);
    LSApplicationWorkspaceRemoteObserver *observer = [workspace remoteObserver];
    NSArray *localObservers = [observer localObservers];
    NSLog(@"local observers: %@", localObservers);
    
    
   // KBHelperClass *helperClass = [KBHelperClass new];
    //[helperClass doMagic];
    
    void *CS_handle = dlopen (CORE_SERVICE_FRAMEWORK, RTLD_NOW);
    if (!CS_handle) { fprintf(stderr,"Can't find %s!\n", CORE_SERVICE_FRAMEWORK); exit(2); };
    
    
    if (strncmp(argv[1], "app",3) == 0)
    {
        
        CFStringRef wantedBundleID = NULL;
        
        if (strcmp(argv[1], "apps")) // that is, not all apps
        {
            // expecting a bundle identifier here
            
            if (!argv[2]) { fprintf(stderr,"app option requires a specific bundle id. Try 'apps' to get a list\n");
                exit(5); }
            
            wantedBundleID = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc,
                                                       argv[2],  // const char *cStr,
                                                       kCFStringEncodingUTF8); //CFStringEncoding encoding);
        }
        
        CFArrayRef apps =  (CFArrayRef) [workspace performSelector:@selector(allApplications)];
        // This is a CFArray
        if (CFGetTypeID(apps) != CFArrayGetTypeID())
        {
            fprintf(stderr, "Was expecting a CFArray of Apps!\n");
            exit(2);
        }
        
        int len = CFArrayGetCount(apps);
        int i = 0;
        for (i = 0; i < len ; i++)
        {
            CFTypeRef app = CFArrayGetValueAtIndex(apps,i);
            
            // Got app: Dump if want all, or if matches id.
            // I'm sure there's some Workspace method I missed to get an app by bundle ID, instead
            // of iterating over all of them..
            
            CFStringRef appID = (CFStringRef) [(id)app performSelector:@selector(applicationIdentifier)];
            
            if (appID && wantedBundleID)
            {
                if (CFEqual(appID, wantedBundleID))
                {
                    CFStringRef dump = dumpApp(app, 1);
                    CFShow(dump);
                    CFRelease(dump);
                    exit(0); // only one match here.
                }
            }
            else
            {
                CFStringRef dump = dumpApp(app, verbose);
                CFShow(dump);
                CFRelease(dump);
                
                
            }
        }
        
        if ( wantedBundleID) {
            fprintf(stderr,"Application with Bundle ID %s not found. Try '%s apps' first, and remember case-sensitivity\n",
                    argv[2], argv[0]);
            exit(2);
        }
        
        exit(0);
    }
    
    if (strcmp(argv[1], "publicurls") == 0)
    {
        CFArrayRef puburls =  (CFArrayRef) [workspace performSelector:@selector(publicURLSchemes)];
        
        
        // This is a CFArray
        if (CFGetTypeID(puburls) != CFArrayGetTypeID())
        {
            fprintf(stderr, "Was expecting a CFArray of publicURLSchemes!\n");
            exit(2);
        }
        
        int len = CFArrayGetCount(puburls);
        int i = 0;
        for (i = 0; i < len ; i++)
        {
            CFStringRef url = CFArrayGetValueAtIndex(puburls,i);
            dumpURL(url, verbose);
            
            
            
        }
        exit(0);
        
    }
    
    if (strcmp(argv[1], "privateurls") == 0)
    {
        CFArrayRef (privurls) = (CFArrayRef) [workspace performSelector:@selector(privateURLSchemes)];
        // This is a CFArray
        if (CFGetTypeID(privurls) != CFArrayGetTypeID())
        {
            fprintf(stderr, "Was expecting a CFArray of privateURLSchemes!\n");
            exit(2);
        }
        
        int len = CFArrayGetCount(privurls);
        int i = 0;
        for (i = 0; i < len ; i++)
        {
            CFStringRef url = CFArrayGetValueAtIndex(privurls,i);
            dumpURL(url, verbose);
            
            
            
        }
        exit(0);
        
    }
    
    
    if (strcmp(argv[1], "plugins") == 0)
    {
        CFArrayRef plugins = (CFArrayRef) [workspace performSelector:@selector(installedPlugins)];
        // This, too, is a CFArray
        if (CFGetTypeID(plugins) != CFArrayGetTypeID())
        {
            fprintf(stderr, "Was expecting a CFArray of plugins!\n");
            exit(2);
        }
        
        int len = CFArrayGetCount(plugins);
        int i = 0;
        for (i = 0; i < len ; i++)
        {
            CFTypeRef plugin = CFArrayGetValueAtIndex(plugins,i);
            dumpPlugin(plugin, verbose);
            
            
            
        }
        exit(0);
    }
    
    if (strcmp(argv[1], "types") == 0)
    {
        // I resort to dlopen/dlsym here to make linking simpler. You don't have to.
        
        typedef CFArrayRef (*UTCopyDeclaredTypeIdentifiersFunc)(void);
        
        UTCopyDeclaredTypeIdentifiersFunc UTCopyDeclaredTypeIdentifiers 
        = dlsym (CS_handle, "_UTCopyDeclaredTypeIdentifiers");
        
        CFArrayRef utis =  UTCopyDeclaredTypeIdentifiers();
        
        // As usual, this is an array..
        int len = CFArrayGetCount(utis);
        int i = 0;
        for (i = 0; i < len ; i++)
        {
            // Seriously, AAPL - couldn't you find a better acronym?
            CFTypeRef uti = CFArrayGetValueAtIndex(utis,i);
            CFShow(uti);
            
            
            
        }
        exit(0);
        
        
        
    }
    
    if (strcmp(argv[1], "whohas") == 0)
    {
        
        if (!argv[2]) {
            fprintf(stderr,"whohas: Option requires an URL Scheme or UTI as an argument!\n");
            exit(3);
        }
        
        
        
        CFStringRef url = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc,
                                                    argv[2],  // const char *cStr, 
                                                    kCFStringEncodingUTF8); //CFStringEncoding encoding);
        
        
        
        dumpURL(url,1);
        
        
        exit(0);
    }
    
    
    
#if 0
    // ignore this part for now
    if (strcmp(argv[1],"claims") == 0)
    {
        
        typedef CFStringRef (*LSCopyClaimedActivityIdentifiersAndDomainsFunc)(CFStringRef, CFStringRef);
        
        LSCopyClaimedActivityIdentifiersAndDomainsFunc 
        LSCopyClaimedActivityIdentifiersAndDomains = dlsym (CS_handle, "_LSCopyClaimedActivityIdentifiersAndDomains");
        
        if (!LSCopyClaimedActivityIdentifiersAndDomains )
        {
            fprintf(stderr,"Unable to find LSCopyClaimedActivityIdentifiersAndDomains ");
            exit(3);
        }
        
        CFStringRef result = LSCopyClaimedActivityIdentifiersAndDomains (NULL, NULL);
        CFShow(result);
        exit(0);
        
        
    }
#endif
    
    
    
    if (strcmp(argv[1],"launch") == 0)
    {
        
        
        if (!argv[2]) {
            fprintf(stderr,"launch: Option requires an argument!\n"); exit(3);
        }
        CFStringRef bundleID = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc,
                                                         argv[2],  // const char *cStr, 
                                                         kCFStringEncodingUTF8); //CFStringEncoding encoding);
        int t =  (int) [workspace performSelector:@selector(openApplicationWithBundleID:) withObject:(id) bundleID ];
        
        fprintf(stderr, "%s %s\n",
                t ? "Launched" : "Unable to launch",
                argv[2]);
        
        exit(0);
        
    }
    
    // And this is the real fun part:
    //
    // The little known "lsregister" utility in OS X is closed source, and un-man(1)ned,
    // But one of its coolest features is "dump" - to dump the LaunchServices Database.
    // Turns out this is a *single* line of code. And guess what -- it works on iOS too :-)
    //
    if (strcmp(argv[1], "dump") == 0)
    {
        
        typedef void (*LSDisplayDataFunc)(char *);
        LSDisplayDataFunc       LSDisplayData;
        
        // If you want to get the raw format of the file, you can dump with this, instead:
        //extern _LSDisplayRawStoreData(char *, int);
        //_LSDisplayRawStoreData("/var/mobile/Library/Caches/com.apple.LaunchServices-134.csstore", (void *) 0xff);
        
        
        LSDisplayData = dlsym (CS_handle, "_LSDisplayData");
        
        if (!LSDisplayData) { fprintf(stderr, "Can't find LSDisplayData! Has Apple removed it by now? :-P"); exit(1);}
        
        // The argument expected here is the name of the CoreServices (LaunchServices)
        // DataStore - in iOS "/var/mobile/Library/Caches/com.apple.LaunchServices-###.csstore"
        // but turns out NULL works on both OS X and iOS!
        
        LSDisplayData("/private/var/containers/Data/System/97D6E4BA-C0BF-408B-AF63-1836844381AE/Library/Caches/com.apple.LaunchServices-175-v2.csstore");
        exit(0);
        
    }
    
}
