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