QTfrontend/NSWorkspace_RBAdditions.m
changeset 2400 2422ea85d100
child 3697 d5b30d6373fc
equal deleted inserted replaced
2399:ddde0ac1472b 2400:2422ea85d100
       
     1 //
       
     2 //  NSWorkspace_RBAdditions.m
       
     3 //  PathProps
       
     4 //
       
     5 //  Created by Rainer Brockerhoff on 10/04/2007.
       
     6 //  Copyright 2007 Rainer Brockerhoff. All rights reserved.
       
     7 //
       
     8 
       
     9 #import "NSWorkspace_RBAdditions.h"
       
    10 #include <IOKit/IOKitLib.h>
       
    11 #include <sys/mount.h>
       
    12 
       
    13 NSString* NSWorkspace_RBfstypename = @"NSWorkspace_RBfstypename";
       
    14 NSString* NSWorkspace_RBmntonname = @"NSWorkspace_RBmntonname";
       
    15 NSString* NSWorkspace_RBmntfromname = @"NSWorkspace_RBmntfromname";
       
    16 NSString* NSWorkspace_RBdeviceinfo = @"NSWorkspace_RBdeviceinfo";
       
    17 NSString* NSWorkspace_RBimagefilepath = @"NSWorkspace_RBimagefilepath";
       
    18 NSString* NSWorkspace_RBconnectiontype = @"NSWorkspace_RBconnectiontype";
       
    19 NSString* NSWorkspace_RBpartitionscheme = @"NSWorkspace_RBpartitionscheme";
       
    20 NSString* NSWorkspace_RBserverURL = @"NSWorkspace_RBserverURL";
       
    21 
       
    22 // This static funtion concatenates two strings, but first checks several possibilities...
       
    23 // like one or the other nil, or one containing the other already.
       
    24 
       
    25 static NSString* AddPart(NSString* first,NSString* second) {
       
    26 	if (!second) {
       
    27 		return first;
       
    28 	}
       
    29 	second = [second stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
       
    30 	if (first) {
       
    31 		if ([first rangeOfString:second options:NSCaseInsensitiveSearch].location==NSNotFound) {
       
    32 			if ([second rangeOfString:first options:NSCaseInsensitiveSearch].location==NSNotFound) {
       
    33 				return [NSString stringWithFormat:@"%@; %@",first,second];
       
    34 			}
       
    35 			return second;
       
    36 		}
       
    37 		return first;
       
    38 	}
       
    39 	return second;
       
    40 }
       
    41 
       
    42 // This static functions recurses "upwards" over the IO registry. Returns strings that are concatenated
       
    43 // and ultimately end up under the NSWorkspace_RBdeviceinfo key.
       
    44 // This isn't too robust in that it assumes that objects returned by the objectForKey methods are
       
    45 // either strings or dictionaries. A "standard" implementations would use either only CoreFoundation and
       
    46 // IOKit calls for this, or do more robust type checking on the returned objects.
       
    47 //
       
    48 // Also notice that this works as determined experimentally in 10.4.9, there's no official docs I could find.
       
    49 // YMMV, and it may stop working in any new version of Mac OS X.
       
    50 
       
    51 static NSString* CheckParents(io_object_t thing,NSString* part,NSMutableDictionary* dict) {
       
    52 	NSString* result = part;
       
    53     io_iterator_t parentsIterator = 0;
       
    54     kern_return_t kernResult = IORegistryEntryGetParentIterator(thing,kIOServicePlane,&parentsIterator);
       
    55     if ((kernResult==KERN_SUCCESS)&&parentsIterator) {
       
    56 		io_object_t nextParent = 0;
       
    57 		while ((nextParent = IOIteratorNext(parentsIterator))) {
       
    58 			NSDictionary* props = nil;
       
    59 			NSString* image = nil;
       
    60 			NSString* partition = nil;
       
    61 			NSString* connection = nil;
       
    62 			kernResult = IORegistryEntryCreateCFProperties(nextParent,(CFMutableDictionaryRef*)&props,kCFAllocatorDefault,0);
       
    63 			if (IOObjectConformsTo(nextParent,"IOApplePartitionScheme")) {
       
    64 				partition = [props objectForKey:@"Content Mask"];
       
    65 			} else if (IOObjectConformsTo(nextParent,"IOMedia")) {
       
    66 				partition = [props objectForKey:@"Content"];
       
    67 			} else if (IOObjectConformsTo(nextParent,"IODiskImageBlockStorageDeviceOutKernel")) {
       
    68 				NSData* data = nil;
       
    69 				if (data = [[props objectForKey:@"Protocol Characteristics"] objectForKey:@"Virtual Interface Location Path"]) {
       
    70 					image = [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding] autorelease];
       
    71 				}
       
    72 			} else if (IOObjectConformsTo(nextParent,"IOHDIXHDDriveInKernel")) {
       
    73 				image = [props objectForKey:@"KDIURLPath"];
       
    74 			}
       
    75 			NSDictionary* subdict;
       
    76 			if (subdict = [props objectForKey:@"Protocol Characteristics"]) {
       
    77 				connection = [subdict objectForKey:@"Physical Interconnect"];
       
    78 			} else {
       
    79 				connection = [props objectForKey:@"Physical Interconnect"];
       
    80 			}
       
    81 			if (connection) {
       
    82 				[dict setObject:AddPart([dict objectForKey:NSWorkspace_RBconnectiontype],connection) forKey:NSWorkspace_RBconnectiontype];
       
    83 			}
       
    84 			if (partition) {
       
    85 				[dict setObject:partition forKey:NSWorkspace_RBpartitionscheme];
       
    86 			}
       
    87 			if (image) {
       
    88 				[dict setObject:image forKey:NSWorkspace_RBimagefilepath];
       
    89 			}
       
    90 			NSString* value;
       
    91 			if (subdict = [props objectForKey:@"Device Characteristics"]) {
       
    92 				if (value = [subdict objectForKey:@"Product Name"]) {
       
    93 					result = AddPart(result,value);
       
    94 				}
       
    95 				if (value = [subdict objectForKey:@"Product Revision Level"]) {
       
    96 					result = AddPart(result,value);
       
    97 				}
       
    98 				if (value = [subdict objectForKey:@"Vendor Name"]) {
       
    99 					result = AddPart(result,value);
       
   100 				}
       
   101 			}
       
   102 			if (value = [props objectForKey:@"USB Serial Number"]) {
       
   103 				result = AddPart(result,value);
       
   104 			}
       
   105 			if (value = [props objectForKey:@"USB Vendor Name"]) {
       
   106 				result = AddPart(result,value);
       
   107 			}
       
   108 			NSString* cls = [(NSString*)IOObjectCopyClass(nextParent) autorelease];
       
   109 			if (![cls isEqualToString:@"IOPCIDevice"]) {
       
   110 			
       
   111 // Uncomment the following line to have the device tree dumped to the console.
       
   112 //				NSLog(@"=================================> %@:%@\n",cls,props);
       
   113 
       
   114 				result = CheckParents(nextParent,result,dict);
       
   115 			}
       
   116 			IOObjectRelease(nextParent);
       
   117 		}
       
   118     }
       
   119     if (parentsIterator) {
       
   120 		IOObjectRelease(parentsIterator);
       
   121     }
       
   122 	return result;
       
   123 }
       
   124 
       
   125 // This formats the (partially undocumented) AFPXMountInfo info into a string.
       
   126 
       
   127 static NSString* FormatAFPURL(AFPXVolMountInfoPtr mountInfo,NSString** devdesc) {
       
   128 	UInt8* work = ((UInt8*)mountInfo)+mountInfo->serverNameOffset;
       
   129 	if (devdesc) {
       
   130 		*devdesc = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease];
       
   131 	}
       
   132 	work = ((UInt8*)mountInfo)+mountInfo->volNameOffset;
       
   133 	NSString* volname = [[[NSString alloc] initWithBytes:&work[1] length:work[0] encoding:NSUTF8StringEncoding] autorelease];
       
   134 	work = ((UInt8*)mountInfo)+mountInfo->alternateAddressOffset;
       
   135 	AFPAlternateAddress* afpa = (AFPAlternateAddress*)work;
       
   136 	AFPTagData* afpta = (AFPTagData*)(&afpa->fAddressList);
       
   137 	NSString* ip = nil;
       
   138 	NSString* dns = nil;
       
   139 	int i = afpa->fAddressCount;
       
   140 	while ((i-->0)) {
       
   141 		switch (afpta->fType) {
       
   142 			case kAFPTagTypeIP:
       
   143 				if (!ip) {
       
   144 					ip = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease];
       
   145 				}
       
   146 				break;
       
   147 			case kAFPTagTypeIPPort:
       
   148 				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])];
       
   149 				break;
       
   150 			case kAFPTagTypeDNS:
       
   151 				dns = [[[NSString alloc] initWithBytes:&afpta->fData[0] length:afpta->fLength-2 encoding:NSUTF8StringEncoding] autorelease];
       
   152 				break;
       
   153 			case 0x07:
       
   154 				ip = [NSString stringWithFormat:@"[%x:%x:%x:%x:%x:%x:%x:%x]",OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[0]),
       
   155 					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[2]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[4]),
       
   156 					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[6]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[8]),
       
   157 					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[10]),OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[12]),
       
   158 					OSSwapBigToHostInt16(*(UInt16*)&afpta->fData[14])];
       
   159 				break;
       
   160 		}
       
   161 		afpta = (AFPTagData*)((char*)afpta+afpta->fLength);
       
   162 	}
       
   163 	return [NSString stringWithFormat:@"afp://%@/%@",dns?:(ip?:@""),volname];
       
   164 }
       
   165 
       
   166 @implementation NSWorkspace (NSWorkspace_RBAdditions)
       
   167 
       
   168 // Returns a NSDictionary with properties for the path. See details in the .h file.
       
   169 // This assumes that the length of path is less than PATH_MAX (currently 1024 characters).
       
   170 
       
   171 - (NSDictionary*)propertiesForPath:(NSString*)path {
       
   172 	const char* ccpath = (const char*)[path fileSystemRepresentation];
       
   173 	NSMutableDictionary* result = nil;
       
   174 	struct statfs fs;
       
   175 	if (!statfs(ccpath,&fs)) {
       
   176 		NSString* from = [NSString stringWithUTF8String:fs.f_mntfromname];
       
   177 		result = [NSMutableDictionary dictionaryWithObjectsAndKeys:
       
   178 			[NSString stringWithUTF8String:fs.f_fstypename],NSWorkspace_RBfstypename,
       
   179 			[NSString stringWithUTF8String:fs.f_mntonname],NSWorkspace_RBmntonname,
       
   180 			nil];
       
   181 		if (strncmp(fs.f_mntfromname,"/dev/",5)==0) {
       
   182 // For a local volume,get the IO registry tree and search it for further info.
       
   183 			mach_port_t masterPort = 0;
       
   184 			io_iterator_t mediaIterator = 0;
       
   185 			kern_return_t kernResult = IOMasterPort(bootstrap_port,&masterPort);
       
   186 			if (kernResult==KERN_SUCCESS) {
       
   187 				CFMutableDictionaryRef classesToMatch = IOBSDNameMatching(masterPort,0,&fs.f_mntfromname[5]);
       
   188 				if (classesToMatch) {
       
   189 					kernResult = IOServiceGetMatchingServices(masterPort,classesToMatch,&mediaIterator);
       
   190 					if ((kernResult==KERN_SUCCESS)&&mediaIterator) {
       
   191 						io_object_t firstMedia = 0;
       
   192 						while ((firstMedia = IOIteratorNext(mediaIterator))) {
       
   193 							NSString* stuff = CheckParents(firstMedia,nil,result);
       
   194 							if (stuff) {
       
   195 								[result setObject:stuff forKey:NSWorkspace_RBdeviceinfo];
       
   196 							}
       
   197 							IOObjectRelease(firstMedia);
       
   198 						}
       
   199 					}
       
   200 				}
       
   201 			}
       
   202 			if (mediaIterator) {
       
   203 				IOObjectRelease(mediaIterator);
       
   204 			}
       
   205 			if (masterPort) {
       
   206 				mach_port_deallocate(mach_task_self(),masterPort);
       
   207 			}
       
   208 		}
       
   209 		//Don't need this for disk images, gets around warnings for some deprecated functions
       
   210 		
       
   211 		/* else {
       
   212 // For a network volume, get the volume reference number and use to get the server URL.
       
   213 			FSRef ref;
       
   214 			if (FSPathMakeRef((const UInt8*)ccpath,&ref,NULL)==noErr) {
       
   215 				FSCatalogInfo info;
       
   216 				if (FSGetCatalogInfo(&ref,kFSCatInfoVolume,&info,NULL,NULL,NULL)==noErr) {
       
   217 					ParamBlockRec pb;
       
   218 					UInt16 vmisize = 0;
       
   219 					VolumeMountInfoHeaderPtr mountInfo = NULL;
       
   220 					pb.ioParam.ioCompletion = NULL;
       
   221 					pb.ioParam.ioNamePtr = NULL;
       
   222 					pb.ioParam.ioVRefNum = info.volume;
       
   223 					pb.ioParam.ioBuffer = (Ptr)&vmisize;
       
   224 					pb.ioParam.ioReqCount = sizeof(vmisize);
       
   225 					if ((PBGetVolMountInfoSize(&pb)==noErr)&&vmisize) {
       
   226 						mountInfo = (VolumeMountInfoHeaderPtr)malloc(vmisize);
       
   227 						if (mountInfo) {
       
   228 							pb.ioParam.ioBuffer = (Ptr)mountInfo;
       
   229 							pb.ioParam.ioReqCount = vmisize;
       
   230 							if (PBGetVolMountInfo(&pb)==noErr) {
       
   231 								NSString* url = nil;
       
   232 								switch (mountInfo->media) {
       
   233 								case AppleShareMediaType:
       
   234 									url = FormatAFPURL((AFPXVolMountInfoPtr)mountInfo,&from);
       
   235 									break;
       
   236 								case 'http':
       
   237 									url = from;
       
   238 									break;
       
   239 								case 'crbm':
       
   240 								case 'nfs_':
       
   241 								case 'cifs':
       
   242 									url = [NSString stringWithUTF8String:(char*)mountInfo+sizeof(VolumeMountInfoHeader)+sizeof(OSType)];
       
   243 									break;
       
   244 								}
       
   245 								if (url) {
       
   246 									[result setObject:url forKey:NSWorkspace_RBserverURL];
       
   247 								}
       
   248 							}
       
   249 						}
       
   250 						free(mountInfo);
       
   251 					}
       
   252 				}
       
   253 			}
       
   254 		}*/
       
   255 		[result setObject:from forKey:NSWorkspace_RBmntfromname];
       
   256 	}
       
   257 	return result;
       
   258 }
       
   259 
       
   260 @end