Attempt to fix
issue #125. The password pop-up doesn't appear every time when going into the official server anymore, now it only does it when the password is blank. If a user enters an invalid password, the password is set blank to avoid the user going back to the official server just to be rejected. When entering an invalid password, the unknown error dialog doesn't show up anymore, but the connection lost to server one still does. This fixes the bug where the user would be spammed with error messages. The user can also now change his password in the settings page.
//
// NSWorkspace_RBAdditions.m
// PathProps
//
// Created by Rainer Brockerhoff on 10/04/2007.
// Copyright 2007 Rainer Brockerhoff. All rights reserved.
//
#import "NSWorkspace_RBAdditions.h"
#include <IOKit/IOKitLib.h>
#include <sys/mount.h>
NSString* NSWorkspace_RBfstypename = @"NSWorkspace_RBfstypename";
NSString* NSWorkspace_RBmntonname = @"NSWorkspace_RBmntonname";
NSString* NSWorkspace_RBmntfromname = @"NSWorkspace_RBmntfromname";
NSString* NSWorkspace_RBdeviceinfo = @"NSWorkspace_RBdeviceinfo";
NSString* NSWorkspace_RBimagefilepath = @"NSWorkspace_RBimagefilepath";
NSString* NSWorkspace_RBconnectiontype = @"NSWorkspace_RBconnectiontype";
NSString* NSWorkspace_RBpartitionscheme = @"NSWorkspace_RBpartitionscheme";
NSString* NSWorkspace_RBserverURL = @"NSWorkspace_RBserverURL";
// This static funtion concatenates two strings, but first checks several possibilities...
// like one or the other nil, or one containing the other already.
static NSString* AddPart(NSString* first,NSString* second) {
if (!second) {
return first;
}
second = [second stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (first) {
if ([first rangeOfString:second options:NSCaseInsensitiveSearch].location==NSNotFound) {
if ([second rangeOfString:first options:NSCaseInsensitiveSearch].location==NSNotFound) {
return [NSString stringWithFormat:@"%@; %@",first,second];
}
return second;
}
return first;
}
return second;
}
// This static functions recurses "upwards" over the IO registry. Returns strings that are concatenated
// and ultimately end up under the NSWorkspace_RBdeviceinfo key.
// This isn't too robust in that it assumes that objects returned by the objectForKey methods are
// either strings or dictionaries. A "standard" implementations would use either only CoreFoundation and
// IOKit calls for this, or do more robust type checking on the returned objects.
//
// Also notice that this works as determined experimentally in 10.4.9, there's no official docs I could find.
// YMMV, and it may stop working in any new version of Mac OS X.
static NSString* CheckParents(io_object_t thing,NSString* part,NSMutableDictionary* dict) {
NSString* result = part;
io_iterator_t parentsIterator = 0;
kern_return_t kernResult = IORegistryEntryGetParentIterator(thing,kIOServicePlane,&parentsIterator);
if ((kernResult==KERN_SUCCESS)&&parentsIterator) {
io_object_t nextParent = 0;
while ((nextParent = IOIteratorNext(parentsIterator))) {
NSDictionary* props = nil;
NSString* image = nil;
NSString* partition = nil;
NSString* connection = nil;
kernResult = IORegistryEntryCreateCFProperties(nextParent,(CFMutableDictionaryRef*)&props,kCFAllocatorDefault,0);
if (IOObjectConformsTo(nextParent,"IOApplePartitionScheme")) {
partition = [props objectForKey:@"Content Mask"];
} else if (IOObjectConformsTo(nextParent,"IOMedia")) {
partition = [props objectForKey:@"Content"];
} else if (IOObjectConformsTo(nextParent,"IODiskImageBlockStorageDeviceOutKernel")) {
NSData* data = nil;
if (data = [[props objectForKey:@"Protocol Characteristics"] objectForKey:@"Virtual Interface Location Path"]) {
image = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease];
}
} else if (IOObjectConformsTo(nextParent,"IOHDIXHDDriveInKernel")) {
image = [props objectForKey:@"KDIURLPath"];
}
NSDictionary* subdict;
if (subdict = [props objectForKey:@"Protocol Characteristics"]) {
connection = [subdict objectForKey:@"Physical Interconnect"];
} else {
connection = [props objectForKey:@"Physical Interconnect"];
}
if (connection) {
[dict setObject:AddPart([dict objectForKey:NSWorkspace_RBconnectiontype],connection) forKey:NSWorkspace_RBconnectiontype];
}
if (partition) {
[dict setObject:partition forKey:NSWorkspace_RBpartitionscheme];
}
if (image) {
[dict setObject:image forKey:NSWorkspace_RBimagefilepath];
}
NSString* value;
if (subdict = [props objectForKey:@"Device Characteristics"]) {
if (value = [subdict objectForKey:@"Product Name"]) {
result = AddPart(result,value);
}
if (value = [subdict objectForKey:@"Product Revision Level"]) {
result = AddPart(result,value);
}
if (value = [subdict objectForKey:@"Vendor Name"]) {
result = AddPart(result,value);
}
}
if (value = [props objectForKey:@"USB Serial Number"]) {
result = AddPart(result,value);
}
if (value = [props objectForKey:@"USB Vendor Name"]) {
result = AddPart(result,value);
}
NSString* cls = [(NSString*)IOObjectCopyClass(nextParent) autorelease];
if (![cls isEqualToString:@"IOPCIDevice"]) {
// Uncomment the following line to have the device tree dumped to the console.
// NSLog(@"=================================> %@:%@\n",cls,props);
result = CheckParents(nextParent,result,dict);
}
IOObjectRelease(nextParent);
}
}
if (parentsIterator) {
IOObjectRelease(parentsIterator);
}
return result;
}
// This formats the (partially undocumented) AFPXMountInfo info into a string.
static NSString* FormatAFPURL(AFPXVolMountInfoPtr mountInfo,NSString** devdesc) {
UInt8* work = ((UInt8*)mountInfo)+mountInfo->serverNameOffset;
if (devdesc) {
*devdesc = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease];
}
work = ((UInt8*)mountInfo)+mountInfo->volNameOffset;
NSString* volname = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease];
work = ((UInt8*)mountInfo)+mountInfo->alternateAddressOffset;
AFPAlternateAddress* afpa = (AFPAlternateAddress*)work;
AFPTagData* afpta = (AFPTagData*)(&afpa->fAddressList);
NSString* ip = nil;
NSString* dns = nil;
int i = afpa->fAddressCount;
while ((i-->0)) {
switch (afpta->fType) {
case kAFPTagTypeIP:
if (!ip) {
ip = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease];
}
break;
case kAFPTagTypeIPPort:
ip = [NSString stringWithFormat:@"%u.%u.%u.%u:%u",afpta->fData[0],afpta->fData[1],afpta->fData[2],afpta->fData[3],OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[4])];
break;
case kAFPTagTypeDNS:
dns = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease];
break;
case 0x07:
ip = [NSString stringWithFormat:@"[%x:%x:%x:%x:%x:%x:%x:%x]",OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[0]),
OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[2]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[4]),
OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[6]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[8]),
OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[10]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[12]),
OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[14])];
break;
}
afpta = (AFPTagData*)((char*)afpta+afpta->fLength);
}
return [NSString stringWithFormat:@"afp://%@/%@",dns?:(ip?:@""),volname];
}
@implementation NSWorkspace (NSWorkspace_RBAdditions)
// Returns a NSDictionary with properties for the path. See details in the .h file.
// This assumes that the length of path is less than PATH_MAX (currently 1024 characters).
- (NSDictionary*)propertiesForPath:(NSString*)path {
const char* ccpath = (const char*)[path fileSystemRepresentation];
NSMutableDictionary* result = nil;
struct statfs fs;
if (!statfs(ccpath,&fs)) {
NSString* from = [NSString stringWithUTF8String:fs.f_mntfromname];
result = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSString stringWithUTF8String:fs.f_fstypename],NSWorkspace_RBfstypename,
[NSString stringWithUTF8String:fs.f_mntonname],NSWorkspace_RBmntonname,
nil];
if (strncmp(fs.f_mntfromname,"/dev/",5)==0) {
// For a local volume,get the IO registry tree and search it for further info.
mach_port_t masterPort = 0;
io_iterator_t mediaIterator = 0;
kern_return_t kernResult = IOMasterPort(bootstrap_port,&masterPort);
if (kernResult==KERN_SUCCESS) {
CFMutableDictionaryRef classesToMatch = IOBSDNameMatching(masterPort,0,&fs.f_mntfromname[5]);
if (classesToMatch) {
kernResult = IOServiceGetMatchingServices(masterPort,classesToMatch,&mediaIterator);
if ((kernResult==KERN_SUCCESS)&&mediaIterator) {
io_object_t firstMedia = 0;
while ((firstMedia = IOIteratorNext(mediaIterator))) {
NSString* stuff = CheckParents(firstMedia,nil,result);
if (stuff) {
[result setObject:stuff forKey:NSWorkspace_RBdeviceinfo];
}
IOObjectRelease(firstMedia);
}
}
}
}
if (mediaIterator) {
IOObjectRelease(mediaIterator);
}
if (masterPort) {
mach_port_deallocate(mach_task_self(),masterPort);
}
}
//Don't need this for disk images, gets around warnings for some deprecated functions
/* else {
// For a network volume, get the volume reference number and use to get the server URL.
FSRef ref;
if (FSPathMakeRef((const UInt8*)ccpath,&ref,NULL)==noErr) {
FSCatalogInfo info;
if (FSGetCatalogInfo(&ref,kFSCatInfoVolume,&info,NULL,NULL,NULL)==noErr) {
ParamBlockRec pb;
UInt16 vmisize = 0;
VolumeMountInfoHeaderPtr mountInfo = NULL;
pb.ioParam.ioCompletion = NULL;
pb.ioParam.ioNamePtr = NULL;
pb.ioParam.ioVRefNum = info.volume;
pb.ioParam.ioBuffer = (Ptr)&vmisize;
pb.ioParam.ioReqCount = sizeof(vmisize);
if ((PBGetVolMountInfoSize(&pb)==noErr)&&vmisize) {
mountInfo = (VolumeMountInfoHeaderPtr)malloc(vmisize);
if (mountInfo) {
pb.ioParam.ioBuffer = (Ptr)mountInfo;
pb.ioParam.ioReqCount = vmisize;
if (PBGetVolMountInfo(&pb)==noErr) {
NSString* url = nil;
switch (mountInfo->media) {
case AppleShareMediaType:
url = FormatAFPURL((AFPXVolMountInfoPtr)mountInfo,&from);
break;
case 'http':
url = from;
break;
case 'crbm':
case 'nfs_':
case 'cifs':
url = [NSString stringWithUTF8String:(char*)mountInfo+sizeof(VolumeMountInfoHeader)+sizeof(OSType)];
break;
}
if (url) {
[result setObject:url forKey:NSWorkspace_RBserverURL];
}
}
}
free(mountInfo);
}
}
}
}*/
[result setObject:from forKey:NSWorkspace_RBmntfromname];
}
return result;
}
@end