|
@@ -0,0 +1,774 @@
|
|
|
+#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);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+}
|