Determine local drive from MDItemRef - objective-c

When I receive a list of MDItemRef items returned from a Spotlight query (in obj-c) I was wondering if there is an easy way to determine if they come from the OS install drive vs. an externally connected USB drive.
The basic premise is that I want to ignore anything from the local drive and only watch for files on external USB drives.
Thanks!

Una sugerencĂ­a simple:
Grab the item's path and see if it's prefixed with "/Volumes/". If it is, then it's on an external device.
Example:
MDItemRef myItem = ...;
NSString * itemPath = (NSString *)MDItemCopyAttribute(myItem, kMDItemPath);
if ([itemPath hasPrefix:#"/Volumes/"]) {
NSLog(#"Found external item");
} else {
NSLog(#"Found internal item");
}
[itemPath release];

The problem with checking for paths in /Volumes is that it also includes internal partitions, like /Volumes/WINDOWS. Also, although rare, external drives can have mount points other than /Volumes
The more correct way is to use FSGetVolumeParms() to get a GetVolParmsInfoBuffer structure that contains information about the volume, like bIsEjectable, bIsRemovable, bIsOnInternalBus.
You can get the FSVolumeRefNum from a FSRef using FSGetCatalogInfo():
FSCatalogInfo info = {0};
OSErr status = FSGetCatalogInfo(&fsRef, kFSCatInfoVolume, &info, nil, nil, nil);
if (status == noErr)
{
_volumeRefNum = info.volume;
}
With the volumeRef, you can get the volume params:
FSGetVolumeParms(_volumeRefNum, &_params, sizeof(_params));
_params is a GetVolParmsInfoBuffer structure that has info such as:
- (BOOL) isEjectable
{
return (_params.vMExtendedAttributes & (1 << bIsEjectable)) != 0;
}
- (BOOL) isRemovable
{
return (_params.vMExtendedAttributes & (1 << bIsRemovable)) != 0;
}
- (BOOL) isAutoMounted
{
return (_params.vMExtendedAttributes & (1 << bIsAutoMounted)) != 0;
}
- (BOOL) isExternal
{
return (_params.vMExtendedAttributes & (1 << bIsOnExternalBus)) != 0;
}
- (BOOL) isInternal
{
return (_params.vMExtendedAttributes & (1 << bIsOnInternalBus)) != 0;
}

Are you only looking on non-boot drives or on external drives (most of the time they mean the same, but they could be different on a on a system with multiple partitions or multiple internal drives (Mac Pro).
If you only want non-internal drives, you can look to see if the if the path is prefixed with a removable drive mount point.
Similar to Dave's code:
MDItemRef myItem = ...;
NSString * itemPath = (NSString *)MDItemCopyAttribute(myItem, kMDItemPath);
NSArray * removableVolumes = [[NSWorkspace sharedWorkspace] mountedRemovableMedia];
BOOL externalVolume = NO;
for (NSString *eachVolume in removableVolumes) {
if ([itemPath hasPrefix: eachVolume]) {
externalVolume = YES;
break;
}
}
Upside - ignores internal drives (if that's what you're going for).
Downside - includes mounted drive images (in your case, if they're Spotlight-indexed, I suppose).
This actually needs a bit of work - it could return a false positive if an internal drive mount point has the same prefix as an external drive - for example, internal drive mounted at "/Volumes/drive_2" and external drive "/Volumes/drive".

Related

How do I create an Inter App MIDI In port

I will program an inter App MIDI In Port in my Arranger App, that can be accessed by other MIDI App's. I would appreciate very much to get some sample code. I built a virtual MIDI In port like this, but how to make it visible for other App's:
MIDIClientRef virtualMidi;
result = MIDIClientCreate(CFSTR("Virtual Client"), MyMIDINotifyProc, NULL, &virtualMidi);
You need to use MIDIDestinationCreate, which will be visible to other MIDI Clients. You need to provide a MIDIReadProc callback that will be notified when a MIDI event arrives to your MIDI Destination. You may create another MIDI Input Port as well, with the same callback, that you can connect yourself from within your own program to an external MIDI Source.
Here is an example (in C++):
void internalCreate(CFStringRef name)
{
OSStatus result = noErr;
result = MIDIClientCreate( name , nullptr, nullptr, &m_client );
if (result != noErr) {
qDebug() << "MIDIClientCreate() err:" << result;
return;
}
result = MIDIDestinationCreate ( m_client, name, MacMIDIReadProc, (void*) this, &m_endpoint );
if (result != noErr) {
qDebug() << "MIDIDestinationCreate() err:" << result;
return;
}
result = MIDIInputPortCreate( m_client, name, MacMIDIReadProc, (void *) this, &m_port );
if (result != noErr) {
qDebug() << "MIDIInputPortCreate() error:" << result;
return;
}
}
Another example, in ObjectiveC from symplesynth
- (id)initWithName:(NSString*)newName
{
PYMIDIManager* manager = [PYMIDIManager sharedInstance];
MIDIEndpointRef newEndpoint;
OSStatus error;
SInt32 newUniqueID;
// This makes sure that we don't get notified about this endpoint until after
// we're done creating it.
[manager disableNotifications];
MIDIDestinationCreate ([manager midiClientRef], (CFStringRef)newName, midiReadProc, self, &newEndpoint);
// This code works around a bug in OS X 10.1 that causes
// new sources/destinations to be created without unique IDs.
error = MIDIObjectGetIntegerProperty (newEndpoint, kMIDIPropertyUniqueID, &newUniqueID);
if (error == kMIDIUnknownProperty) {
newUniqueID = PYMIDIAllocateUniqueID();
MIDIObjectSetIntegerProperty (newEndpoint, kMIDIPropertyUniqueID, newUniqueID);
}
MIDIObjectSetIntegerProperty (newEndpoint, CFSTR("PYMIDIOwnerPID"), [[NSProcessInfo processInfo] processIdentifier]);
[manager enableNotifications];
self = [super initWithMIDIEndpointRef:newEndpoint];
ioIsRunning = NO;
return self;
}
Ports can't be discovered from the API, but sources and destinations can. You want to create a MIDISource or MIDIDestination so that MIDI clients can call MIDIGetNumberOfDestinations/MIDIGetDestination or MIDIGetNumberOfSources/MIDIGetSource and discover it.
FYI, there is no need to do what you are planning to do on macOS because the IAC driver already does it. If this is for iOS, these are the steps to follow:
Create at least one MIDI Client.
Create a MIDIInputPort with a read block for I/O.
Use MIDIPortConnectSource to attach the input port to every MIDI Source of interest.
[From now, every MIDI message received by the source will come to your read block.]
If you want to resend this data to a different destination, you'll need to have created a MIDIOutputPort as well. Use MIDISend with that port to the desired MIDI Destination.

Get description of network adapter using Swift [duplicate]

I am trying to get a list of active network interfaces with end user understandable names. Like the names listed in System Preferences instead of en0 en5.
I have the raw interfaces using getifaddrs but haven't been able to find how to take those and get the system names of Ethernet or Wifi.
Anyone know how to do this? This would be for macOS.
What I have now:
struct ifaddrs *ifap;
if( getifaddrs(&ifap) == 0 ){
struct ifaddrs *interface;
for (interface = ifap; interface != NULL; interface = interface->ifa_next) {
unsigned int flags = interface->ifa_flags;
struct sockaddr *addr = interface->ifa_addr;
// Check for running IPv4, IPv6 interfaces. Skip the loopback interface.
if ((flags & (IFF_UP|IFF_RUNNING|IFF_LOOPBACK)) == (IFF_UP|IFF_RUNNING)) {
if (addr->sa_family == AF_INET || addr->sa_family == AF_INET6) {
// Convert interface address to a human readable string:
char host[NI_MAXHOST];
getnameinfo(addr, addr->sa_len, host, sizeof(host), NULL, 0, NI_NUMERICHOST);
printf("interface:%s, address:%s\n", interface->ifa_name, host);
// MAGIC HERE TO CONVERT ifa_name to "Ethernet" or something
}
}
}
freeifaddrs(ifap);
This is possible with System Configuration on macOS. In Objective-C like so:
CFArrayRef ref = SCNetworkInterfaceCopyAll();
NSArray* networkInterfaces = (__bridge NSArray *)(ref);
for(int i = 0; i < networkInterfaces.count; i += 1) {
SCNetworkInterfaceRef interface = (__bridge SCNetworkInterfaceRef)(networkInterfaces[i]);
CFStringRef displayName = SCNetworkInterfaceGetLocalizedDisplayName(interface);
CFStringRef bsdName = SCNetworkInterfaceGetBSDName(interface);
NSLog(#"Name:%# \ninterface: %#\nbsd:%#",displayName, SCNetworkInterfaceGetInterfaceType(interface), bsdName);
}
The localized display name will be something like Display Ethernet or WiFi and the BSD name will be something like en5 which will allow matching to the above code.
This approach doesn't work on iOS, but there aren't really any other configurations on iOS anyway.

Given a Process ID, determine if the process is a windowed process on a Mac

Need a way to programmatically determine if a process is a windowed process using the process ID. This needs to work for both user and system processes.
With the crude method below, one could determine if a user process is windowed. However, this has a major flaw, it will only work for user processes, not system.
- (BOOL)processIsWindowed:(pid_t)processID {
for (NSRunningApplication app in [[NSWorkspace sharedWorkspace] runningApplications]) {
if(app.processIdentifier == processID && (app.activationPolicy == NSApplicationActivationPolicyRegular)){
return YES;
}
}
return NO;
}
Using:
static int GetBSDProcessList(kinfo_proc **procList, size_t *procCount){}
from
Using NSWorkspace to get all running processes
will list all processes, but I can't immediatgely see a way to determine if it is a windowed process.
A process listed by the above method has flags (i.e. process->kp_proc.p_flags) but I don't see any flags listed: https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/proc.h that might indicate it as a windowed process.
Here's how you can determine if a process has a window:
The UiProcesses() method will create an array of processIDs for processes with windows.
CFArrayRef UiProcesses()
{
CFArrayRef orderedwindows = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID);
CFIndex count = CFArrayGetCount (orderedwindows);
CFMutableArrayRef uiProcess = CFArrayCreateMutable (kCFAllocatorDefault , count, &kCFTypeArrayCallBacks);
for (CFIndex i = 0; i < count; i++)
{
if (orderedwindows)
{
CFDictionaryRef windowsdescription = (CFDictionaryRef)CFArrayGetValueAtIndex(orderedwindows, i);
CFNumberRef windowownerpid = (CFNumberRef)CFDictionaryGetValue (windowsdescription, CFSTR("kCGWindowOwnerPID"));
CFArrayAppendValue (uiProcess, windowownerpid);
}
}
return uiProcess;
}
Source: How to Identify if the process in User Interface Process?

iOS/Objective-C: library to connect to POP3

I'd like to connect to IMAP and POP3 servers, for IMAP I'm currently using MailCore. Unfortunately I don't find a suitable POP3-framwork.
I tried with libetpan:
mailpop3 * pop3;
int r;
pop3 = mailpop3_new(0, NULL);
r = mailpop3_ssl_connect(pop3, "pop.gmail.com", 995);
check_error(r, "connect failed");
but I always get a connection refused error; and it's only C, I would prefer Objective-C. Even better would be a library which I could use for both; IMAP and POP3.
I haven't used OCMail, but it seems like it's what you're looking for. It claims to support "POP3, IMAP4, SMTP, POPS, IMAPS, SMTPS".
Edit: Build Error
Turns out, the solution is actually in the README file.
Once you've downloaded the ZIP from Github, open the Xcode project.
Build for Profiling (Product Menu > Build For > Profiling (Command-Shift-I)).
Open Xcode preferences and go to "Locations"
Under Derived Data, next to the Advanced button you'll see a file path (something like /Users/YourUserName/Library/Developer/Xcode/DerivedData). There'll be a little arrow next to the path; click the arrow to go to that location in Finder.
It'll take you to a folder with all of your Xcode projects. Find the folder whose name starts with OCMail (and has a bunch of gibberish after it).
In that folder, find Build > Products > Debug-iphoneos > libOCMail.a. That's the library file you'll want to add into your Xcode project. Just drag it into your Xcode project and you should be good to go.
I got a bunch of errors building the project. They came from a badly defined enum type. Here's a cleaned up file:
http://cl.ly/code/442x2x3X3Y2I
Just download and replace the existing MimeMessage.m file before you build.
I was working with libetpan in past and I was connecting to pop3 server without problems, so I checked if it still working. I used code from here: https://github.com/dinhviethoa/libetpan/blob/master/tests/pop-sample.c and adjusted it for iOS.
If You use it, You will see a lot of warnings and app will crash after fetching first message, but connecting is working (of course, You need to enter Your email login and password).
I'm not saying that libetpan is good solution. When I was developing app with mail support I also used mailcore for IMAP and eventually resigned from POP3 support. But if You run from options it could be useful.
static void check_error(int r, char * msg)
{
if (r == MAILPOP3_NO_ERROR)
return;
fprintf(stderr, "%s\n", msg);
exit(EXIT_FAILURE);
}
-(IBAction)testButtonClick:(id)sender
{
mailpop3 * pop3;
int r;
carray * list;
unsigned int i;
// if (argc < 3) {
// fprintf(stderr, "syntax: pop-sample [gmail-email-address] [gmail- password]\n");
// exit(EXIT_FAILURE);
// }
mkdir("download", 0700);
pop3 = mailpop3_new(0, NULL);
r = mailpop3_ssl_connect(pop3, "pop.gmail.com", 995);
check_error(r, "connect failed");
r = mailpop3_user(pop3, #"mail login".cString);
check_error(r, "user failed");
r = mailpop3_pass(pop3, #"mail password".cString);
check_error(r, "pass failed");
r = mailpop3_list(pop3, &list);
check_error(r, "list failed");
NSLog(#"carray_count(list_: %d", carray_count(list));
for(i = 0 ; i < carray_count(list) ; i ++) {
struct mailpop3_msg_info * info;
char * msg_content;
size_t msg_size;
FILE * f;
char filename[512];
struct stat stat_info;
info = (mailpop3_msg_info *) carray_get(list, i);
if (info->msg_uidl == NULL) {
continue;
}
snprintf(filename, sizeof(filename), "download/%s.eml", info->msg_uidl);
r = stat(filename, &stat_info);
if (r == 0) {
printf("already fetched %u %s\n", info->msg_index, info->msg_uidl);
continue;
}
if(msg_content != NULL)
NSLog(#"msg_content: %#", [NSString stringWithUTF8String:msg_content]);
r = mailpop3_retr(pop3, info->msg_index, &msg_content, &msg_size);
check_error(r, "get failed");
// f = fopen(filename, "w");
// fwrite(msg_content, 1, msg_size, f);
// fclose(f);
// mailpop3_retr_free(msg_content);
if (info->msg_uidl != NULL) {
printf("fetched %u %s\n", info->msg_index, info->msg_uidl);
}
else {
printf("fetched %u\n", info->msg_index);
}
}
mailpop3_quit(pop3);
mailpop3_free(pop3);
// exit(EXIT_SUCCESS);
}

dispatch_source_get_data does not return correct flag when monitoring a directory

Can someone please tell me what is this code not working ? it always return a DISPATH_VNODE_WRITE while monitoring application documents directory in iOS 6.0. (iPad) Below is my code. It returns 0x2 always no matter a file is deleted or renamed or added :(. Is this because it is a directory that I am monitoring ? not a file !!! is there any way I can find out what caused the directory to send notification ?
int directoryFileDescripter = open([documentDirectory UTF8String], O_EVTONLY);
if (directoryFileDescripter < 0) {
NSLog(#"Couldn't obtain file descripter from the system.");
return;
}
dispatch_queue_t mainQueue = /*dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);*/dispatch_get_main_queue();
if (mainQueue == NULL) {
NSLog(#"Couldn't obtain mainQueue from the system.");
close(directoryFileDescripter);
return;
}
dispSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, directoryFileDescripter, DISPATCH_VNODE_WRITE | DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME, mainQueue);
if (dispSource == NULL) {
NSLog(#"Couldn't obtain dispatch source for directory from the system.");
close(directoryFileDescripter);
return;
}
dispatch_source_set_event_handler(dispSource, ^{
NSLog(#"directory notification received.");
int fileDes = dispatch_source_get_handle(dispSource);
unsigned long mask = dispatch_source_get_data(dispSource);
char path[PATH_MAX] = {0};
int nRes = fcntl(fileDes, F_GETPATH, &path);
if (nRes < 0) {
return;
}
if (mask & DISPATCH_VNODE_WRITE) {
NSLog(#"A file has been written.");
}
if (mask & DISPATCH_VNODE_DELETE) {
NSLog(#"A file has been deleted.");
}
if (mask & DISPATCH_VNODE_RENAME) {
NSLog(#"A file has been renamed.");
}
});
dispatch_source_set_cancel_handler(dispSource, ^{
close(directoryFileDescripter);
});
dispatch_resume(dispSource);
You are correct about the "why." When files are created, renamed, or deleted, the directory is modified. You are watching the directory, so you get a "WRITE" event.
I would typically deal with this by re-scanning the directory each time it is marked as written, and noting the changes yourself. If you're not worried about the directory itself moving or being deleted, you can just watch for WRITE events.
You can of course also watch each file's VNODE, but I expect this would be much more complicated to implement well for this kind of problem.