(MACOSX) - Knowing whether a mounted device was mounted from a DMG - cocoa

Couldn't find that info using DiskArbitration or FSGetVolumeInfo/GetVolumeParms...
I know that hdiutil uses a private framework called DiskImages framework, but I wouldn't want to run an external utility each time I want this info... wheres the API for this ?

July 2015 Update
This update was prompted by Stan James' new question.
You can obtain this information using the DiskArbitration framework. To use the example below, you must link against and #import it.
#import <DiskArbitration/DiskArbitration.h>
...
- (BOOL)isDMGVolumeAtURL:(NSURL *)url
{
BOOL isDMG = NO;
if (url.isFileURL) {
DASessionRef session = DASessionCreate(kCFAllocatorDefault);
if (session != nil) {
DADiskRef disk = DADiskCreateFromVolumePath(kCFAllocatorDefault, session, (__bridge CFURLRef)url);
if (disk != nil) {
NSDictionary * desc = CFBridgingRelease(DADiskCopyDescription(disk));
NSString * model = desc[(NSString *)kDADiskDescriptionDeviceModelKey];
isDMG = ([model isEqualToString:#"Disk Image"]);
CFRelease(disk);
}
CFRelease(session);
}
}
return isDMG;
}
Usage:
BOOL isDMG = [someObject isDMGVolumeAtURL:[NSURL fileURLWithPath:#"/Volumes/Some Volume"]];
I hope this helps.

Related

OSX: Export system certificates from keychain in PEM format programmatically

How can I extract all root CA certificates from all the keychains on OSX programmatically in pem format?
Keychain programming services should allow this but how?
Any help would be appreciable.
security find-certificate -a -p /System/Library/Keychains/SystemRootCertificates.keychain >certs-roots.pem
security find-certificate -a -p /Library/Keychains/System.keychain >certs-system.pem
security find-certificate -a -p ~/Library/Keychains/login.keychain-db >certs-user.pem
BTW: You can see those paths in Keychain Access when you hover over the Keychains list (top/left).
You can combine system and user pems by using the default certificate source
security find-certificate -a -p >certs.pem
This is super useful for node.js, when you want to use require('https').request on typical corporate internal stuff without having to resort to hacks like accepting any certificate without checking. You don't need to include the system roots since nodejs has you covered already for those.
NODE_EXTRA_CA_CERTS=certs.pem node
Answering my own question:
On OSX you can invoke a NSTask to get response from the security command line utility:
security find-certificate -a -p /System/Library/Keychains/SystemCACertificates.keychain > allcerts.pem
Hey I know I'm late to this but I came across the same problem today and spent many hours figuring out how to do this. I know that the original poster might not need to know this anymore but hopefully this helps someone.
The following is my code to exactly replicate what you've done without using the command line.
+ (NSURL *)createCertsFileInDirectory:(NSURL *)directory {
NSString *outPath = [directory path];
if (!outPath) {
return nil;
}
outPath = [outPath stringByAppendingPathComponent:#"allcerts.pem"];
NSURL * outURL = [NSURL fileURLWithPath:outPath];
SecKeychainRef keychain;
if (SecKeychainOpen("/System/Library/Keychains/SystemCACertificates.keychain", &keychain) != errSecSuccess) {
return nil;
}
CFMutableArrayRef searchList = CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
CFArrayAppendValue(searchList, keychain);
CFTypeRef keys[] = { kSecClass, kSecMatchLimit, kSecAttrCanVerify, kSecMatchSearchList };
CFTypeRef values[] = { kSecClassCertificate, kSecMatchLimitAll, kCFBooleanTrue, searchList };
CFDictionaryRef dict = CFDictionaryCreate(kCFAllocatorDefault, keys, values, 4, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFTypeRef results;
OSStatus status = SecItemCopyMatching(dict, &results);
CFArrayRef arr = (CFArrayRef) results;
NSLog(#"total item count = %ld", CFArrayGetCount(arr));
CFRelease(dict);
CFRelease(searchList);
CFRelease(keychain);
if (status != errSecSuccess) {
return nil;
}
CFDataRef certsData;
status = SecItemExport(results, kSecFormatPEMSequence, kSecItemPemArmour, NULL, &certsData);
CFRelease(results);
if (status != errSecSuccess) {
return nil;
}
NSData *topLevelData = (NSData *) CFBridgingRelease(certsData);
if (![topLevelData writeToURL:outURL atomically:YES]) {
return nil;
}
return outURL;
}

How to get an ALAsset URL from a PHAsset?

You can do it sneakily† using the undocumented PHAsset.ALAssetURL property, but I'm looking for something documented.
† In Objective-C, this will help
#interface PHAsset (Sneaky)
#property (nonatomic, readonly) NSURL *ALAssetURL;
#end
Create the assetURL by leveraging the localidentifier of the PHAsset.
Example:
PHAsset.localidentifier returns 91B1C271-C617-49CE-A074-E391BA7F843F/L0/001
Now take the 32 first characters to build the assetURL, like:
assets-library://asset/asset.JPG?id=91B1C271-C617-49CE-A074-E391BA7F843F&ext=JPG
You might change the extension JPG depending on the UTI of the asset (requestImageDataForAsset returns the UTI), but in my testing the extensions of the assetURL seems to be ignored anyhow.
I wanted to be able to get a URL for an asset too. However, I have realised that the localIdentifier can be persisted instead and used to recover the PHAsset.
PHAsset* asset = [PHAsset fetchAssetsWithLocalIdentifiers:#[localIdentifier] options:nil].firstObject;
Legacy asset URLs can be converted using:
PHAsset* legacyAsset = [PHAsset fetchAssetsWithALAssetUrls:#[assetUrl] options:nil].firstObject;
NSString* convertedIdentifier = legacyAsset.localIdentifier;
(before that method gets obsoleted...)
(Thanks holtmann - localIdentifier is hidden away in PHObject.)
Here is working code tested on iOS 11 both simulator and device
PHFetchResult *result = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
[result enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
PHAsset *asset = (PHAsset *)obj;
[asset requestContentEditingInputWithOptions:nil completionHandler:^(PHContentEditingInput * _Nullable contentEditingInput, NSDictionary * _Nonnull info) {
NSLog(#"URL:%#", contentEditingInput.fullSizeImageURL.absoluteString);
NSString* path = [contentEditingInput.fullSizeImageURL.absoluteString substringFromIndex:7];//screw all the crap of file://
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isExist = [fileManager fileExistsAtPath:path];
if (isExist)
NSLog(#"oh yeah");
else {
NSLog(#"damn");
}
}];
}];
Read the bottom!
The resultHandler for PHImageManager.requestImage returns 2 objects: result and info.
You can get the original filename for the PHAsset (like IMG_1043.JPG) as well as its full path on the filesystem with:
let url = info?["PHImageFileURLKey"] as! URL
This should work right, but for some reason it doesn't. So basically, you have to copy your image to a file then access that then delete it.
The PHImageFileURLKey is usable to get the original file name, but you cannot actually access that file. It probably has to do with the fact that code in the background can access the file while other apps can delete it.
Here is a PHAsset extension written in Swift that will retrieve the URL.
extension PHAsset {
func getURL(completionHandler : #escaping ((_ responseURL : URL?) -> Void)){
if self.mediaType == .image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
return true
}
self.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) -> Void in
completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
})
} else if self.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) -> Void in
if let urlAsset = asset as? AVURLAsset {
let localVideoUrl: URL = urlAsset.url as URL
completionHandler(localVideoUrl)
} else {
completionHandler(nil)
}
})
}
}
}

NSURL from PHAsset

I'm converting our app over to use the Photos Framework of iOS8, the ALAsset framework is clearly a second class citizen under iOS8.
I'm having a problem is that our architecture really wants an NSURL that represents the location of the media on "disk." We use this to upload the media to our servers for further processing.
This was easy with ALAsset:
ALAssetRepresentation *rep = [asset defaultRepresentation];
self.originalVideo = rep.url;
But I'm just not seeing this ability in PHAsset. I guess I can call:
imageManager.requestImageDataForAsset
and then write it out to a temp spot in the file system but that seems awfully heavyweight and wasteful, not to mention potentially slow.
Is there a way to get this or am I going to have refactor more of my app to only use NSURLs for iOS7 and some other method for iOS8?
If you use [imageManager requestAVAssetForVideo...], it'll return an AVAsset. That AVAsset is actually an AVURLAsset, so if you cast it, you can access it's -url property.
I'm not sure if you can create a new asset out of this, but it does give you the location.
SWIFT 2.0 version
This function returns NSURL from PHAsset (both image and video)
func getAssetUrl(mPhasset : PHAsset, completionHandler : ((responseURL : NSURL?) -> Void)){
if mPhasset.mediaType == .Image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
return true
}
mPhasset.requestContentEditingInputWithOptions(options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [NSObject : AnyObject]) -> Void in
completionHandler(responseURL : contentEditingInput!.fullSizeImageURL)
})
} else if mPhasset.mediaType == .Video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .Original
PHImageManager.defaultManager().requestAVAssetForVideo(mPhasset, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [NSObject : AnyObject]?) -> Void in
if let urlAsset = asset as? AVURLAsset {
let localVideoUrl : NSURL = urlAsset.URL
completionHandler(responseURL : localVideoUrl)
} else {
completionHandler(responseURL : nil)
}
})
}
}
If you have a PHAsset, you can get the url for said asset like this:
[asset requestContentEditingInputWithOptions:editOptions
completionHandler:^(PHContentEditingInput *contentEditingInput, NSDictionary *info) {
NSURL *imageURL = contentEditingInput.fullSizeImageURL;
}];
Use the new localIdentifier property of PHObject. (PHAsset inherits from this).
It provides similar functionality to an ALAsset URL, namely that you can load assets by calling the method
+[PHAsset fetchAssetsWithLocalIdentifiers:identifiers options:options]
All the above solutions won't work for slow-motion videos. A solution that I found handles all video asset types is this:
func createFileURLFromVideoPHAsset(asset: PHAsset, destinationURL: NSURL) {
PHCachingImageManager().requestAVAssetForVideo(self, options: nil) { avAsset, _, _ in
let exportSession = AVAssetExportSession(asset: avAsset!, presetName: AVAssetExportPresetHighestQuality)!
exportSession.outputFileType = AVFileTypeMPEG4
exportSession.outputURL = destinationURL
exportSession.exportAsynchronouslyWithCompletionHandler {
guard exportSession.error == nil else {
log.error("Error exporting video asset: \(exportSession.error)")
return
}
// It worked! You can find your file at: destinationURL
}
}
}
See this answer here.
And this one here.
In my experience you'll need to first export the asset to disk in order to get a fully accessible / reliable URL.
The answers linked to above describe how to do this.
Just want to post the hidden gem from a comment from #jlw
#rishu1992 For slo-mo videos, grab the AVComposition's
AVCompositionTrack (of mediaType AVMediaTypeVideo), grab its first
segment (of type AVCompositionTrackSegment), and then access its
sourceURL property. – jlw Aug 25 '15 at 11:52
In speking of url from PHAsset, I had once prepared a util func on Swift 2 (although only for playing videos from PHAsset). Sharing it in this answer, might help someone.
static func playVideo (view:UIViewController, asset:PHAsset)
Please check this Answer
Here's a handy PHAsset category:
#implementation PHAsset (Utils)
- (NSURL *)fileURL {
__block NSURL *url = nil;
switch (self.mediaType) {
case PHAssetMediaTypeImage: {
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = YES;
[PHImageManager.defaultManager requestImageDataForAsset:self
options:options
resultHandler:^(NSData *imageData, NSString *dataUTI, UIImageOrientation orientation, NSDictionary *info) {
url = info[#"PHImageFileURLKey"];
}];
break;
}
case PHAssetMediaTypeVideo: {
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[PHImageManager.defaultManager requestAVAssetForVideo:self
options:nil
resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:AVURLAsset.class]) {
url = [(AVURLAsset *)asset URL];
}
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
break;
}
default:
break;
}
return url;
}
#end
I had similiar problem with video files, what worked for me was:
NSString* assetID = [asset.localIdentifier substringToIndex:(asset.localIdentifier.length - 7)];
NSURL* videoURL = [NSURL URLWithString:[NSString stringWithFormat:#"assets-library://asset/asset.mov?id=%#&ext=mov", assetID]];
Where asset is PHAsset.

Execute Applescript from Cocoa App with params

I would like to know how to execute an applescript from a cocoa application passing parameters.
I have seen how easy is to execute applescripts with no parameters in other questions here at stackoverflow, however the use NSAppleScript class, in which, i haven't seen no method that solve my problem. Does anyone have any idea.
I would like a Cocoa code with the same effect o this shell:
osascript teste.applescript "snow:Users:MyUser:Desktop:MyFolder" "snow:Users:MyUser:Desktop:Example:"
So it may run this AppleScript.
on run argv
set source to (item 1 of argv)
set destiny to (item 2 of argv)
tell application "Finder" to make new alias file at destiny to source
0
end run
Any help is appreciated. Thanks in advance.
Look at my GitHub repository, I have a category of NSAppleEventDescriptor that makes it much easier to create NSAppleEventDescriptor to call different AppleScript procedures with arguments, and coercion to and from many AppleScript typed.
NSAppleEventDescriptor-NDCoercion
I found easier to follow this piece code. I took a code from here and modified it to my purpose.
- (BOOL) executeScriptWithPath:(NSString*)path function:(NSString*)functionName andArguments:(NSArray*)scriptArgumentArray
{
BOOL executionSucceed = NO;
NSAppleScript * appleScript;
NSAppleEventDescriptor * thisApplication, *containerEvent;
NSURL * pathURL = [NSURL fileURLWithPath:path];
NSDictionary * appleScriptCreationError = nil;
appleScript = [[NSAppleScript alloc] initWithContentsOfURL:pathURL error:&appleScriptCreationError];
if (appleScriptCreationError)
{
NSLog([NSString stringWithFormat:#"Could not instantiate applescript %#",appleScriptCreationError]);
}
else
{
if (functionName && [functionName length])
{
/* If we have a functionName (and potentially arguments), we build
* an NSAppleEvent to execute the script. */
//Get a descriptor for ourself
int pid = [[NSProcessInfo processInfo] processIdentifier];
thisApplication = [NSAppleEventDescriptor descriptorWithDescriptorType:typeKernelProcessID
bytes:&pid
length:sizeof(pid)];
//Create the container event
//We need these constants from the Carbon OpenScripting framework, but we don't actually need Carbon.framework...
#define kASAppleScriptSuite 'ascr'
#define kASSubroutineEvent 'psbr'
#define keyASSubroutineName 'snam'
containerEvent = [NSAppleEventDescriptor appleEventWithEventClass:kASAppleScriptSuite
eventID:kASSubroutineEvent
targetDescriptor:thisApplication
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
//Set the target function
[containerEvent setParamDescriptor:[NSAppleEventDescriptor descriptorWithString:functionName]
forKeyword:keyASSubroutineName];
//Pass arguments - arguments is expecting an NSArray with only NSString objects
if ([scriptArgumentArray count])
{
NSAppleEventDescriptor *arguments = [[NSAppleEventDescriptor alloc] initListDescriptor];
NSString *object;
for (object in scriptArgumentArray) {
[arguments insertDescriptor:[NSAppleEventDescriptor descriptorWithString:object]
atIndex:([arguments numberOfItems] + 1)]; //This +1 seems wrong... but it's not
}
[containerEvent setParamDescriptor:arguments forKeyword:keyDirectObject];
[arguments release];
}
//Execute the event
NSDictionary * executionError = nil;
NSAppleEventDescriptor * result = [appleScript executeAppleEvent:containerEvent error:&executionError];
if (executionError != nil)
{
NSLog([NSString stringWithFormat:#"error while executing script. Error %#",executionError]);
}
else
{
NSLog(#"Script execution has succeed. Result(%#)",result);
executionSucceed = YES;
}
}
else
{
NSDictionary * executionError = nil;
NSAppleEventDescriptor * result = [appleScript executeAndReturnError:&executionError];
if (executionError != nil)
{
NSLog([NSString stringWithFormat:#"error while executing script. Error %#",executionError]);
}
else
{
NSLog(#"Script execution has succeed. Result(%#)",result);
executionSucceed = YES;
}
}
}
[appleScript release];
return executionSucceed;
}
Technical Note TN2084
Using AppleScript Scripts in Cocoa Applications
Even though your application is written in Objective-C using Cocoa, you can use AppleScript scripts to perform certain operations. This Technical Note explains how to integrate and execute AppleScripts from within your Cocoa application. It discusses how to leverage the NSAppleScript class and the use of NSAppleEventDescriptor to send data to the receiver.
https://developer.apple.com/library/archive/technotes/tn2084/_index.html
https://applescriptlibrary.files.wordpress.com/2013/11/technical-note-tn2084-using-applescript-scripts-in-cocoa-applications.pdf
Swift 4 version, modified from the code here:
https://gist.github.com/chbeer/3666e4b7b2e71eb47b15eaae63d4192f
import Carbon
static func runAppleScript(_ url: URL) {
var appleScriptError: NSDictionary? = nil
guard let script = NSAppleScript(contentsOf: url, error: &appleScriptError) else {
return
}
let message = NSAppleEventDescriptor(string: "String parameter")
let parameters = NSAppleEventDescriptor(listDescriptor: ())
parameters.insert(message, at: 1)
var psn = ProcessSerialNumber(highLongOfPSN: UInt32(0), lowLongOfPSN: UInt32(kCurrentProcess))
let target = NSAppleEventDescriptor(descriptorType: typeProcessSerialNumber, bytes: &psn, length: MemoryLayout<ProcessSerialNumber>.size)
let handler = NSAppleEventDescriptor(string: "MyMethodName")
let event = NSAppleEventDescriptor.appleEvent(withEventClass: AEEventClass(kASAppleScriptSuite), eventID: AEEventID(kASSubroutineEvent), targetDescriptor: target, returnID: AEReturnID(kAutoGenerateReturnID), transactionID: AETransactionID(kAnyTransactionID))
event.setParam(handler, forKeyword: AEKeyword(keyASSubroutineName))
event.setParam(parameters, forKeyword: AEKeyword(keyDirectObject))
var executeError: NSDictionary? = nil
script.executeAppleEvent(event, error: &executeError)
if let executeError = executeError {
print("ERROR: \(executeError)")
}
}
For running the apple script:
on MyMethodName(theParameter)
display dialog theParameter
end MyMethodName
I'm not all too familiar with AppleScript, but I seem to remember that they are heavily based on (the rather crappy) Apple Events mechanism which dates back to the days where the 56k Modem was the coolest Gadget in your house.
Therefore I'd guess that you're looking for executeAppleEvent:error: which is part of NSAppleScript. Maybe you can find some information on how to encapsulate execution arguments in the instance of NSAppleEventDescriptor that you have to pass along with this function.

Unique Identifier of a Mac?

On an iPhone I can use
[[UIDevice currentDevice] uniqueIdentifier];
to get a string which identifies this device. Is there anything equal in OSX ? I didn't find anything. I just want to identify the Mac which started the application. Can you help me ?
Apple has a technote on uniquely identifying a mac. Here's a loosely modified version of the code Apple has posted in that technote... don't forget to link your project against IOKit.framework in order to build this:
#import <IOKit/IOKitLib.h>
- (NSString *)serialNumber
{
io_service_t platformExpert = IOServiceGetMatchingService(kIOMasterPortDefault,
IOServiceMatching("IOPlatformExpertDevice"));
CFStringRef serialNumberAsCFString = NULL;
if (platformExpert) {
serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert,
CFSTR(kIOPlatformSerialNumberKey),
kCFAllocatorDefault, 0);
IOObjectRelease(platformExpert);
}
NSString *serialNumberAsNSString = nil;
if (serialNumberAsCFString) {
serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
CFRelease(serialNumberAsCFString);
}
return serialNumberAsNSString;
}
Swift 2 Answer
This answer augments Jarret Hardie's 2011 answer. It's a Swift 2 String extension. I've added inline comments to explain what I did and why, since navigating whether or not an object needs to be released can be tricky here.
extension String {
static func macSerialNumber() -> String {
// Get the platform expert
let platformExpert: io_service_t = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice"));
// Get the serial number as a CFString ( actually as Unmanaged<AnyObject>! )
let serialNumberAsCFString = IORegistryEntryCreateCFProperty(platformExpert, kIOPlatformSerialNumberKey, kCFAllocatorDefault, 0);
// Release the platform expert (we're responsible)
IOObjectRelease(platformExpert);
// Take the unretained value of the unmanaged-any-object
// (so we're not responsible for releasing it)
// and pass it back as a String or, if it fails, an empty string
return (serialNumberAsCFString.takeUnretainedValue() as? String) ?? ""
}
}
Alternatively, the function could return String? and the last line could not return an empty string. That might make it easier to recognize the extreme situations where the serial number could not be retrieved (such as the repaired-Mac-motherboard scenario harrisg mentioned in his comment to Jerret's answer).
I also verified proper memory management with Instruments.
I hope someone finds it useful!
Thanks. Works perfectly after changing
serialNumberAsNSString = [NSString stringWithString:(NSString *)serialNumberAsCFString];
TO
serialNumberAsNSString = [NSString stringWithString:(__bridge NSString *)serialNumberAsCFString];
the __bridge is recommended by Xcode itself.

Resources