| File: | Plugins/Bonjour/libezv/Classes/EKEzvIncomingFileTransfer.m |
| Location: | line 301, column 3 |
| Description: | Memory Leak |
| Code is compiled without garbage collection. |
| 1 | // |
| 2 | // EKEzvIncomingFileTransfer.m |
| 3 | // Adium |
| 4 | // |
| 5 | // Created by Erich Kreutzer on 8/14/07. |
| 6 | // |
| 7 | |
| 8 | #import "EKEzvIncomingFileTransfer.h" |
| 9 | |
| 10 | #define APPLE_SINGLE_HEADER_LENGTH 26 |
| 11 | #define APPLE_SINGLE_MAGIC_NUMBER 0x00051600 |
| 12 | #define APPLE_SINGLE_VERSION_NUMBER 0x00020000 |
| 13 | |
| 14 | #define AS_ENTRY_DATA_FORK 1 |
| 15 | #define AS_ENTRY_RESOURCE_FORK 2 |
| 16 | #define AS_ENTRY_REAL_NAME 3 |
| 17 | #define AS_ENTRY_COMMENT 4 |
| 18 | #define AS_ENTRY_ICON_BW 5 |
| 19 | #define AS_ENTRY_ICON_COLOR 6 |
| 20 | #define AS_ENTRY_DATE_INFO 8 |
| 21 | #define AS_ENTRY_FINDER_INFO 9 |
| 22 | #define AS_ENTRY_MACINTOSH_FILE_INFO 10 |
| 23 | #define AS_ENTRY_PRODOS_FILE_INFO 11 |
| 24 | #define AS_ENTRY_MSDOS_FILE_INFO 12 |
| 25 | #define AS_ENTRY_AFP_SHORT_NAME 13 |
| 26 | #define AS_ENTRY_AFP_FILE_INFO 14 |
| 27 | #define AS_ENTRY_AFP_DIRECTORY_ID 15 |
| 28 | |
| 29 | struct AppleSingleHeader { |
| 30 | UInt32 magicNumber; |
| 31 | UInt32 versionNumber; |
| 32 | char filler[16]; |
| 33 | UInt16 numberEntries; |
| 34 | }; |
| 35 | typedef struct AppleSingleHeader AppleSingleHeader; |
| 36 | |
| 37 | struct AppleSingleEntry { |
| 38 | UInt32 entryID; |
| 39 | UInt32 offset; |
| 40 | UInt32 length; |
| 41 | }; |
| 42 | typedef struct AppleSingleEntry AppleSingleEntry; |
| 43 | |
| 44 | struct AppleSingleFinderInfo { |
| 45 | struct FileInfo finderInfo; |
| 46 | struct FXInfo extendedFinderInfo; |
| 47 | }; |
| 48 | typedef struct AppleSingleFinderInfo AppleSingleFinderInfo; |
| 49 | |
| 50 | @implementation EKEzvIncomingFileTransfer |
| 51 | #pragma mark Downloading |
| 52 | |
| 53 | - (void)dealloc |
| 54 | { |
| 55 | [currentDownloads release]; |
| 56 | [encodedDownloads release]; |
| 57 | [super dealloc]; |
| 58 | } |
| 59 | - (void) startDownload |
| 60 | { |
| 61 | currentDownloads = [[NSMutableArray alloc] initWithCapacity: 10]; |
| 62 | encodedDownloads = [[NSMutableArray alloc] initWithCapacity: 10]; |
| 63 | if (type == EKEzvFile_Transfer) { |
| 64 | [self downloadFile]; |
| 65 | } else if (type == EKEzvDirectory_Transfer) { |
| 66 | [self downloadFolder]; |
| 67 | } else { |
| 68 | [[[manager client] client] reportError:@"Don't know what type of item we are downloading" ofLevel:AWEzvError]; |
| 69 | [[[manager client] client] remoteCanceledFileTransfer:self]; |
| 70 | } |
| 71 | |
| 72 | } |
| 73 | |
| 74 | - (void) cancelDownload |
| 75 | { |
| 76 | if ([currentDownloads count] > 0) { |
| 77 | NSEnumerator *enumerator = [currentDownloads objectEnumerator]; |
| 78 | NSURLDownload *download; |
| 79 | while ( download = [enumerator nextObject]) { |
| 80 | [download cancel]; |
| 81 | } |
| 82 | [currentDownloads release]; currentDownloads = nil0; |
| 83 | [encodedDownloads release]; encodedDownloads = nil0; |
| 84 | } |
| 85 | } |
| 86 | - (void) downloadFolder |
| 87 | { |
| 88 | /*We need to first get the xml for the layout */ |
| 89 | NSURL *URL = [NSURL URLWithString:url]; |
| 90 | NSError *error = nil0; |
| 91 | NSXMLDocument *documentRoot = [[[NSXMLDocument alloc] initWithContentsOfURL:URL options:0 error:&error] autorelease]; |
| 92 | if (error) { |
| 93 | [[[[self manager] client] client] remoteCanceledFileTransfer:self]; |
| 94 | return; |
| 95 | } |
| 96 | /*NO error so we have the xml */ |
| 97 | NSXMLElement *root = [documentRoot rootElement]; |
| 98 | /*We don't care about the root name because the user can rename it*/ |
| 99 | NSString *posixFlags = [[root attributeForName:@"posixflags"] objectValue]; |
| 100 | |
| 101 | NSFileManager *fileManager = [NSFileManager defaultManager]; |
| 102 | |
| 103 | bool_Bool isDirectory = NO( BOOL ) 0; |
| 104 | bool_Bool exists = [fileManager fileExistsAtPath:localFilename isDirectory:&isDirectory]; |
| 105 | if (exists && isDirectory) { |
| 106 | /*We need to remove this file*/ |
| 107 | if (![fileManager removeFileAtPath:localFilename handler:nil0]) { |
| 108 | [[[[self manager] client] client] reportError:@"Could not replace old file at path" ofLevel:AWEzvError]; |
| 109 | [[[[self manager] client] client] remoteCanceledFileTransfer:self]; |
| 110 | return; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | if (![fileManager createDirectoryAtPath:localFilename attributes:[self posixAttributesFromString:posixFlags]]) { |
| 115 | [[[[self manager] client] client] reportError:@"There was an error creating the root directory for the file tranfer" ofLevel:AWEzvError]; |
| 116 | [[[[self manager] client] client] remoteCanceledFileTransfer:self]; |
| 117 | return; |
| 118 | } |
| 119 | |
| 120 | |
| 121 | bool_Bool folderSuccess = YES( BOOL ) 1; |
| 122 | bool_Bool fileSuccess = YES( BOOL ) 1; |
| 123 | |
| 124 | itemsToDownload = [NSMutableDictionary dictionaryWithCapacity:10]; |
| 125 | permissionsToApply = [[NSMutableDictionary alloc] initWithCapacity:10]; |
| 126 | |
| 127 | /*Call downloadFolder:path:url: for dir children */ |
| 128 | NSEnumerator *enumerator = [[root elementsForName:@"dir"] objectEnumerator]; |
| 129 | NSXMLElement *nextElement; |
| 130 | while (nextElement = [enumerator nextObject]) { |
| 131 | folderSuccess = [self downloadFolder:nextElement path:localFilename url:[self url]]; |
| 132 | } |
| 133 | |
| 134 | /*Call downloadFolder:path:url: for file children */ |
| 135 | enumerator = [[root elementsForName:@"file"] objectEnumerator]; |
| 136 | while (nextElement = [enumerator nextObject]) { |
| 137 | fileSuccess = [self downloadFolder:nextElement path:localFilename url:[self url]]; |
| 138 | } |
| 139 | |
| 140 | if (folderSuccess && fileSuccess) { |
| 141 | |
| 142 | /*Now go through itemsToDownload and download the files*/ |
| 143 | NSEnumerator *enumerator = [itemsToDownload keyEnumerator]; |
| 144 | NSString *path; |
| 145 | NSURL *url; |
| 146 | while ((path = [enumerator nextObject])) { |
| 147 | /* code that uses the returned key */ |
| 148 | url = [itemsToDownload valueForKey:path]; |
| 149 | if (url) { |
| 150 | [self downloadURL:url toPath:path]; |
| 151 | url = nil0; |
| 152 | } else { |
| 153 | [[[[self manager] client] client] reportError:[NSString stringWithFormat:@"Error downloading file from %@ to %@", url, path] ofLevel:AWEzvError]; |
| 154 | [[[[self manager] client] client] remoteCanceledFileTransfer:self]; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | |
| 159 | [permissionsToApply retain]; |
| 160 | } else { |
| 161 | [[[[self manager] client] client] remoteCanceledFileTransfer:self]; |
| 162 | } |
| 163 | } |
| 164 | - (bool_Bool)downloadFolder:(NSXMLElement *)root path:(NSString *)rootPath url:(NSString *)rootURL |
| 165 | { |
| 166 | /*Helper method to recursively download a folder using the xml*/ |
| 167 | /*root will be the current folder or file to download */ |
| 168 | /*rootPath will be the path -without- root's name appended */ |
| 169 | if ([[root name] isEqualToString:@"file"]) { |
| 170 | /*We have a file so get it's info and then download it*/ |
| 171 | NSString *mimeType = [[root attributeForName:@"mimetype"] objectValue]; |
| 172 | NSString *posixFlags = [[root attributeForName:@"posixflags"] objectValue]; |
| 173 | NSString *hfsFlags = [[root attributeForName:@"hfsflags"] objectValue]; |
| 174 | NSString *size = [[root attributeForName:@"size"] objectValue]; |
| 175 | |
| 176 | NSArray *nameChildren = [root elementsForName:@"name"]; |
| 177 | if (!nameChildren) { |
| 178 | [[[[self manager] client] client] reportError:@"Could not download file because there is no name" ofLevel:AWEzvError]; |
| 179 | return NO( BOOL ) 0; |
| 180 | } |
| 181 | NSString *name = [[nameChildren objectAtIndex:0] stringValue]; |
| 182 | NSString *newPath = [rootPath stringByAppendingPathComponent:name]; |
| 183 | NSString *newURL = [rootURL stringByAppendingPathComponent:[name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
| 184 | |
| 185 | /*Download file to newPath from newURL*/ |
| 186 | [itemsToDownload setValue:[NSURL URLWithString:newURL] forKey:newPath]; |
| 187 | [permissionsToApply setValue:[self posixAttributesFromString:posixFlags] forKey:newPath]; |
| 188 | |
| 189 | return YES( BOOL ) 1; |
| 190 | |
| 191 | } else if ([[root name] isEqualToString:@"dir"]) { |
| 192 | /*We have a directory so crete the directory then recursively create the files/dirs */ |
| 193 | NSString *posixFlags = [[root attributeForName:@"posixflags"] objectValue]; |
| 194 | |
| 195 | /*Find the name of the directory*/ |
| 196 | NSArray *nameChildren = [root elementsForName:@"name"]; |
| 197 | if (!nameChildren) { |
| 198 | [[[[self manager] client] client] reportError:@"Could not download directory because there was no name." ofLevel: AWEzvError]; |
| 199 | return NO( BOOL ) 0; |
| 200 | } |
| 201 | NSString *name = [[nameChildren objectAtIndex:0] stringValue]; |
| 202 | |
| 203 | /* Create the directory */ |
| 204 | NSFileManager *defaultManager = [NSFileManager defaultManager]; |
| 205 | NSString *newPath = [rootPath stringByAppendingPathComponent:name]; |
| 206 | |
| 207 | if (![defaultManager createDirectoryAtPath:newPath attributes:[self posixAttributesFromString:posixFlags]]) { |
| 208 | [[[[self manager] client] client] reportError:@"Could not create directory for transfer." ofLevel: AWEzvError]; |
| 209 | |
| 210 | return NO( BOOL ) 0; |
| 211 | } |
| 212 | |
| 213 | |
| 214 | bool_Bool folderSuccess = YES( BOOL ) 1; |
| 215 | bool_Bool fileSuccess = YES( BOOL ) 1; |
| 216 | /* Now call downloadFolder for dir and file children */ |
| 217 | NSString *newURL = [rootURL stringByAppendingPathComponent:[name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; |
| 218 | NSEnumerator *enumerator = [[root elementsForName:@"dir"] objectEnumerator]; |
| 219 | NSXMLElement *nextElement; |
| 220 | while (nextElement = [enumerator nextObject]) { |
| 221 | folderSuccess = [self downloadFolder:nextElement path:newPath url:newURL]; |
| 222 | } |
| 223 | enumerator = [[root elementsForName:@"file"] objectEnumerator]; |
| 224 | while (nextElement = [enumerator nextObject]) { |
| 225 | fileSuccess = [self downloadFolder:nextElement path:newPath url:newURL]; |
| 226 | } |
| 227 | return (fileSuccess && folderSuccess); |
| 228 | } else{ |
| 229 | [[[[self manager] client] client] reportError:@"Error, attempting to download something which is not a directory or a file." ofLevel: AWEzvError]; |
| 230 | |
| 231 | return NO( BOOL ) 0; |
| 232 | } |
| 233 | return NO( BOOL ) 0; |
| 234 | } |
| 235 | - (void) downloadFile |
| 236 | { |
| 237 | [self downloadURL:[NSURL URLWithString:url] toPath:localFilename]; |
| 238 | } |
| 239 | |
| 240 | #pragma mark Download Helper Methods |
| 241 | |
| 242 | /*Download helpers*/ |
| 243 | - (NSDictionary *)posixAttributesFromString:(NSString *)posixFlags |
| 244 | { |
| 245 | NSDictionary *attributes = NULL( ( void * ) 0 ); |
| 246 | if (posixFlags) { |
| 247 | NSScanner *scanner; |
| 248 | int tempInt; |
| 249 | |
| 250 | scanner = [NSScanner scannerWithString:posixFlags]; |
| 251 | [scanner scanHexInt:&tempInt]; |
| 252 | NSNumber *number = [NSNumber numberWithInt:tempInt]; |
| 253 | |
| 254 | attributes = [NSMutableDictionary dictionary]; |
| 255 | [attributes setValue:number forKey:@"NSFilePosixPermissions"]; |
| 256 | } |
| 257 | return attributes; |
| 258 | |
| 259 | } |
| 260 | - (BOOL) applyPermissions |
| 261 | { |
| 262 | /*Now go through and apply the permissions*/ |
| 263 | if (!permissionsToApply) { |
| 264 | return YES( BOOL ) 1; |
| 265 | } |
| 266 | if ([permissionsToApply count] <= 0) { |
| 267 | [permissionsToApply release]; permissionsToApply = nil0; |
| 268 | return YES( BOOL ) 1; |
| 269 | } |
| 270 | NSEnumerator *enumerator = [permissionsToApply keyEnumerator]; |
| 271 | NSString *path; |
| 272 | NSDictionary *attributes; |
| 273 | NSFileManager *defaultManager = [NSFileManager defaultManager]; |
| 274 | while ((path = [enumerator nextObject])) { |
| 275 | /* code that uses the returned key */ |
| 276 | attributes = [permissionsToApply valueForKey:path]; |
| 277 | if (![defaultManager changeFileAttributes:attributes atPath:path]) { |
| 278 | [[[manager client] client] reportError:[NSString stringWithFormat:@"Error applying permissions of %@ to file at %@", attributes, path] ofLevel: AWEzvError]; |
| 279 | [[[manager client] client] remoteCanceledFileTransfer:self]; |
| 280 | [permissionsToApply release]; permissionsToApply = nil0; |
| 281 | return NO( BOOL ) 0; |
| 282 | } |
| 283 | } |
| 284 | [permissionsToApply release]; permissionsToApply = nil0; |
| 285 | return YES( BOOL ) 1; |
| 286 | } |
| 287 | - (void)downloadURL:(NSURL *)url toPath:(NSString *)path |
| 288 | { |
| 289 | /* This should be easy. We have a url and a location so let's download things to a location! */ |
| 290 | |
| 291 | NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; |
| 292 | NSString *value = [NSString stringWithString:@"AppleSingle"]; |
| 293 | [theRequest addValue:value forHTTPHeaderField:@"Accept-Encoding"]; |
| 294 | |
| 295 | // create the connection with the request |
| 296 | // and start loading the data |
[1] Method returns an object with a +1 retain count (owning reference). | |
| 297 | NSURLDownload *theDownload = [[NSURLDownload alloc] initWithRequest:theRequest delegate:self]; |
[2] Taking true branch. | |
| 298 | if (theDownload) { |
| 299 | [currentDownloads addObject:theDownload]; |
| 300 | // set the destination file now |
[3] Object allocated on line 297 and stored into 'theDownload' is no longer referenced after this point and has a retain count of +1 (object leaked). | |
| 301 | [theDownload setDestination:path allowOverwrite:YES( BOOL ) 1]; |
| 302 | } else { |
| 303 | // inform the user that the download could not be made |
| 304 | [[[manager client] client] reportError:@"Error starting download of file transfer." ofLevel: AWEzvError]; |
| 305 | [[[manager client] client] remoteCanceledFileTransfer:self]; |
| 306 | } |
| 307 | |
| 308 | } |
| 309 | |
| 310 | #pragma mark NSURLDownload Delegate Methods |
| 311 | - (void)download:(NSURLDownload *)download didFailWithError:(NSError *)error |
| 312 | { |
| 313 | [[[manager client] client] remoteCanceledFileTransfer:self]; |
| 314 | // inform the user |
| 315 | [[[manager client] client] reportError:[NSString stringWithFormat: @"Download failed! Error - %@ %@", |
| 316 | [error localizedDescription], |
| 317 | [[error userInfo] objectForKey:NSErrorFailingURLStringKey]] ofLevel: AWEzvError]; |
| 318 | [download release]; |
| 319 | } |
| 320 | - (void)downloadDidFinish:(NSURLDownload *)download |
| 321 | { |
| 322 | /*This will get called even when not all of the file has been downloaded so need to check bytes received */ |
| 323 | |
| 324 | /*Let's look up the local file and then decode *if* it is an AppleSingle file*/ |
| 325 | NSURL *itemURL = [[download request] URL]; |
| 326 | if ([encodedDownloads containsObject:itemURL]) { |
| 327 | NSString *itemPath = [self urlToPath:itemURL]; |
| 328 | BOOL decoded = [self decodeAppleSingleAtPath: itemPath]; |
| 329 | if (!decoded) { |
| 330 | [[[manager client] client] remoteCanceledFileTransfer: self]; |
| 331 | } |
| 332 | } |
| 333 | percentComplete=((float)bytesReceived/(float)size); |
| 334 | BOOL success = TRUE1; |
| 335 | if (percentComplete >= 1.0) { |
| 336 | success = [self applyPermissions]; |
| 337 | } |
| 338 | if (success) |
| 339 | [[[manager client] client] updateProgressForFileTransfer:self percent:[NSNumber numberWithFloat:percentComplete] bytesSent:[NSNumber numberWithLongLong:bytesReceived]]; |
| 340 | |
| 341 | [currentDownloads removeObject:download]; |
| 342 | [download release]; |
| 343 | } |
| 344 | - (void)download:(NSURLDownload *)download didReceiveResponse:(NSURLResponse *)response |
| 345 | { |
| 346 | NSDictionary *headers = [(NSHTTPURLResponse *)response allHeaderFields]; |
| 347 | if ([(NSString *)[headers objectForKey:@"Content-Encoding"] isEqualToString:@"AppleSingle"]) { |
| 348 | [encodedDownloads addObject: [[download request] URL]]; |
| 349 | } |
| 350 | } |
| 351 | - (void)download:(NSURLDownload *)download didReceiveDataOfLength:(unsigned)length |
| 352 | { |
| 353 | bytesReceived=bytesReceived+length; |
| 354 | percentComplete=((float)bytesReceived/(float)size); |
| 355 | if (percentComplete >= 1.0) { |
| 356 | /*This will prevent Adium from believing that the download is complete before possible decoding */ |
| 357 | return; |
| 358 | } |
| 359 | [[[manager client] client] updateProgressForFileTransfer:self percent:[NSNumber numberWithFloat:percentComplete] bytesSent:[NSNumber numberWithLongLong:bytesReceived]]; |
| 360 | } |
| 361 | |
| 362 | #pragma mark Encoding Helper Methods |
| 363 | |
| 364 | - (NSString *)urlToPath:(NSURL *)itemURL |
| 365 | { |
| 366 | NSString *urlString = [itemURL absoluteString]; |
| 367 | if ([urlString hasPrefix:url]) { |
| 368 | /*Remove the base url from the string*/ |
| 369 | NSRange range = [urlString rangeOfString:url]; |
| 370 | NSString *path = [urlString substringFromIndex:(range.location + range.length)]; |
| 371 | path = [path stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; |
| 372 | if (localFilename) { |
| 373 | path = [localFilename stringByAppendingPathComponent:path]; |
| 374 | return path; |
| 375 | } else { |
| 376 | return NULL( ( void * ) 0 ); |
| 377 | } |
| 378 | } |
| 379 | return NULL( ( void * ) 0 ); |
| 380 | } |
| 381 | |
| 382 | - (BOOL)decodeAppleSingleAtPath:(NSString *)path |
| 383 | { |
| 384 | /*Get NSData from path*/ |
| 385 | if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { |
| 386 | [[[manager client] client] reportError:@"AppleSingle: Could not apply permissions to file because it does not exist." ofLevel: AWEzvError]; |
| 387 | return NO( BOOL ) 0; |
| 388 | } |
| 389 | NSData *data = [[NSFileManager defaultManager] contentsAtPath:path]; |
| 390 | |
| 391 | /*Declarations*/ |
| 392 | unsigned long length = [data length]; |
| 393 | size_t offset; |
| 394 | struct AppleSingleFinderInfo info; |
| 395 | struct AppleSingleHeader header; |
| 396 | struct AppleSingleEntry entry; |
| 397 | NSRange resourceRange; |
| 398 | BOOL resourceExist = NO( BOOL ) 0; |
| 399 | offset = 0; |
| 400 | |
| 401 | |
| 402 | if (length < APPLE_SINGLE_HEADER_LENGTH26 ) { |
| 403 | [[[manager client] client] reportError:@"AppleSingle: Invalid AppleSingle File." ofLevel: AWEzvError]; |
| 404 | return NO( BOOL ) 0; |
| 405 | } |
| 406 | [data getBytes:&header length:APPLE_SINGLE_HEADER_LENGTH26]; |
| 407 | offset += APPLE_SINGLE_HEADER_LENGTH26; |
| 408 | |
| 409 | /* switch items to host from network byteorder*/ |
| 410 | header.magicNumber = ntohl( __builtin_constant_p ( header . magicNumber ) ? ( ( uint32_t ) ( ( ( ( uint32_t ) ( header . magicNumber ) & 0xff000000 ) >> 24 ) | ( ( ( uint32_t ) ( header . magicNumber ) & 0x00ff0000 ) >> 8 ) | ( ( ( uint32_t ) ( header . magicNumber ) & 0x0000ff00 ) << 8 ) | ( ( ( uint32_t ) ( header . magicNumber ) & 0x000000ff ) << 24 ) ) ) : _OSSwapInt32 ( header . magicNumber ) )(header.magicNumber); |
| 411 | header.versionNumber = ntohl( __builtin_constant_p ( header . versionNumber ) ? ( ( uint32_t ) ( ( ( ( uint32_t ) ( header . versionNumber ) & 0xff000000 ) >> 24 ) | ( ( ( uint32_t ) ( header . versionNumber ) & 0x00ff0000 ) >> 8 ) | ( ( ( uint32_t ) ( header . versionNumber ) & 0x0000ff00 ) << 8 ) | ( ( ( uint32_t ) ( header . versionNumber ) & 0x000000ff ) << 24 ) ) ) : _OSSwapInt32 ( header . versionNumber ) )(header.versionNumber); |
| 412 | header.numberEntries = ntohs( __builtin_constant_p ( header . numberEntries ) ? ( ( uint16_t ) ( ( ( ( uint16_t ) ( header . numberEntries ) & 0xff00 ) >> 8 ) | ( ( ( uint16_t ) ( header . numberEntries ) & 0x00ff ) << 8 ) ) ) : _OSSwapInt16 ( header . numberEntries ) )(header.numberEntries); |
| 413 | |
| 414 | if (!(header.magicNumber == APPLE_SINGLE_MAGIC_NUMBER0x00051600 && header.versionNumber == APPLE_SINGLE_VERSION_NUMBER0x00020000)) { |
| 415 | [[[manager client] client] reportError:@"AppleSingle: Supposed AppleSingle file is not AppleSingle." ofLevel: AWEzvError]; |
| 416 | return NO( BOOL ) 0; |
| 417 | } |
| 418 | /* The magicNumber and versionNumber are correct so we have an AppleSingle file */ |
| 419 | /*Now let's read the entries */ |
| 420 | for (unsigned i = 0; i < header.numberEntries; ++i) |
| 421 | { |
| 422 | if (length < (offset + sizeof(entry))) { |
| 423 | [[[manager client] client] reportError:@"AppleSingle: Not enough reoom for declared number of entries." ofLevel: AWEzvError]; |
| 424 | |
| 425 | return NO( BOOL ) 0; |
| 426 | } |
| 427 | [data getBytes:&entry range: NSMakeRange(offset, sizeof(entry))]; |
| 428 | offset += sizeof(entry); |
| 429 | |
| 430 | /* switch items to host from network byteorder*/ |
| 431 | entry.entryID = ntohl( __builtin_constant_p ( entry . entryID ) ? ( ( uint32_t ) ( ( ( ( uint32_t ) ( entry . entryID ) & 0xff000000 ) >> 24 ) | ( ( ( uint32_t ) ( entry . entryID ) & 0x00ff0000 ) >> 8 ) | ( ( ( uint32_t ) ( entry . entryID ) & 0x0000ff00 ) << 8 ) | ( ( ( uint32_t ) ( entry . entryID ) & 0x000000ff ) << 24 ) ) ) : _OSSwapInt32 ( entry . entryID ) )(entry.entryID); |
| 432 | entry.offset = ntohl( __builtin_constant_p ( entry . offset ) ? ( ( uint32_t ) ( ( ( ( uint32_t ) ( entry . offset ) & 0xff000000 ) >> 24 ) | ( ( ( uint32_t ) ( entry . offset ) & 0x00ff0000 ) >> 8 ) | ( ( ( uint32_t ) ( entry . offset ) & 0x0000ff00 ) << 8 ) | ( ( ( uint32_t ) ( entry . offset ) & 0x000000ff ) << 24 ) ) ) : _OSSwapInt32 ( entry . offset ) )(entry.offset); |
| 433 | entry.length = ntohl( __builtin_constant_p ( entry . length ) ? ( ( uint32_t ) ( ( ( ( uint32_t ) ( entry . length ) & 0xff000000 ) >> 24 ) | ( ( ( uint32_t ) ( entry . length ) & 0x00ff0000 ) >> 8 ) | ( ( ( uint32_t ) ( entry . length ) & 0x0000ff00 ) << 8 ) | ( ( ( uint32_t ) ( entry . length ) & 0x000000ff ) << 24 ) ) ) : _OSSwapInt32 ( entry . length ) )(entry.length); |
| 434 | /*Validate the entry*/ |
| 435 | if (entry.entryID == 0) { |
| 436 | [[[manager client] client] reportError:@"AppleSingle: Invalid Entry ID of value 0." ofLevel: AWEzvError]; |
| 437 | return NO( BOOL ) 0; |
| 438 | } |
| 439 | |
| 440 | if (entry.offset > length) { |
| 441 | [[[manager client] client] reportError:@"AppleSingle: Invalid AppleSingle Encoding." ofLevel: AWEzvError]; |
| 442 | |
| 443 | return NO( BOOL ) 0; |
| 444 | } |
| 445 | |
| 446 | if ((entry.offset + entry.length) > length) { |
| 447 | [[[manager client] client] reportError:@"AppleSingle: Invalid AppleSingle Encoding." ofLevel: AWEzvError]; |
| 448 | return NO( BOOL ) 0; |
| 449 | } |
| 450 | switch(entry.entryID) { |
| 451 | case AS_ENTRY_DATA_FORK1: |
| 452 | //NSLog(@"AS_ENTRY_DATA_FORK"); |
| 453 | resourceRange = NSMakeRange(entry.offset, entry.length); |
| 454 | resourceExist = YES( BOOL ) 1; |
| 455 | break; |
| 456 | case AS_ENTRY_RESOURCE_FORK2: |
| 457 | //NSLog(@"AS_ENTRY_RESOURCE_FORK"); |
| 458 | resourceRange = NSMakeRange(entry.offset, entry.length); |
| 459 | resourceExist = YES( BOOL ) 1; |
| 460 | break; |
| 461 | case AS_ENTRY_FINDER_INFO9: |
| 462 | //NSLog(@"AS_ENTRY_FINDER_INFO"); |
| 463 | [data getBytes:&info range:NSMakeRange(entry.offset, entry.length)]; |
| 464 | info.finderInfo.finderFlags = ntohs( __builtin_constant_p ( info . finderInfo . finderFlags ) ? ( ( uint16_t ) ( ( ( ( uint16_t ) ( info . finderInfo . finderFlags ) & 0xff00 ) >> 8 ) | ( ( ( uint16_t ) ( info . finderInfo . finderFlags ) & 0x00ff ) << 8 ) ) ) : _OSSwapInt16 ( info . finderInfo . finderFlags ) )(info.finderInfo.finderFlags); |
| 465 | break; |
| 466 | case AS_ENTRY_REAL_NAME3: |
| 467 | // NSLog(@"AS_ENTRY_REAL_NAME"); |
| 468 | break; |
| 469 | case AS_ENTRY_COMMENT4: |
| 470 | // NSLog(@"AS_ENTRY_COMMENT"); |
| 471 | break; |
| 472 | case AS_ENTRY_ICON_BW5: |
| 473 | // NSLog(@"AS_ENTRY_ICON_BW"); |
| 474 | break; |
| 475 | case AS_ENTRY_ICON_COLOR6: |
| 476 | // NSLog(@"AS_ENTRY_ICON_COLOR"); |
| 477 | break; |
| 478 | case AS_ENTRY_DATE_INFO8: |
| 479 | // NSLog(@"AS_ENTRY_DATE_INFO"); |
| 480 | break; |
| 481 | case AS_ENTRY_MACINTOSH_FILE_INFO10: |
| 482 | // NSLog(@"AS_ENTRY_MACINTOSH_FILE_INFO"); |
| 483 | break; |
| 484 | case AS_ENTRY_PRODOS_FILE_INFO11: |
| 485 | // NSLog(@"AS_ENTRY_PRODOS_FILE_INFO"); |
| 486 | break; |
| 487 | case AS_ENTRY_MSDOS_FILE_INFO12: |
| 488 | // NSLog(@"AS_ENTRY_MSDOS_FILE_INFO"); |
| 489 | break; |
| 490 | case AS_ENTRY_AFP_SHORT_NAME13: |
| 491 | // NSLog(@"AS_ENTRY_AFP_SHORT_NAME"); |
| 492 | break; |
| 493 | case AS_ENTRY_AFP_FILE_INFO14: |
| 494 | // NSLog(@"AS_ENTRY_AFP_FILE_INFO"); |
| 495 | break; |
| 496 | case AS_ENTRY_AFP_DIRECTORY_ID15: |
| 497 | // NSLog(@"AS_ENTRY_AFP_DIRECTORY_ID"); |
| 498 | break; |
| 499 | default: |
| 500 | // NSLog(@"default"); |
| 501 | break; |
| 502 | } |
| 503 | } |
| 504 | |
| 505 | /*Now we can write the date and apply the attributes */ |
| 506 | if (resourceExist) { |
| 507 | NSData *decodedData = [data subdataWithRange: resourceRange]; |
| 508 | if (![decodedData writeToFile: path atomically:YES( BOOL ) 1]) { |
| 509 | [[[manager client] client] reportError:@"AppleSingle: Could not write decoded data." ofLevel: AWEzvError]; |
| 510 | |
| 511 | } |
| 512 | /*Now apply attributes */ |
| 513 | FSRef ref; |
| 514 | OSStatus err; |
| 515 | BOOL isDirectory = NO( BOOL ) 0; |
| 516 | err = FSPathMakeRef((const UInt8 *)[path fileSystemRepresentation], &ref, &isDirectory); |
| 517 | if (err != noErr) { |
| 518 | [[[manager client] client] reportError:@"AppleSingle: Error creating FSRef" ofLevel: AWEzvError]; |
| 519 | |
| 520 | return NO( BOOL ) 0; |
| 521 | } |
| 522 | struct FSCatalogInfo catalogInfo; |
| 523 | memset(&catalogInfo, 0, sizeof(catalogInfo)); |
| 524 | BlockMoveData(&(info.finderInfo), &(catalogInfo.finderInfo), sizeof((info.finderInfo))); |
| 525 | OSErr error = FSSetCatalogInfo(/*(const FSRef *)*/ &ref, |
| 526 | /*(FSCatalogInfoBitmap)*/ (kFSCatInfoFinderInfo), |
| 527 | /*(const FSCatalogInfo *)*/ &catalogInfo); |
| 528 | if (error != noErr) { |
| 529 | [[[manager client] client] reportError:@"AppleSingle: Error setting catalog info." ofLevel: AWEzvError]; |
| 530 | |
| 531 | return NO( BOOL ) 0; |
| 532 | } |
| 533 | } |
| 534 | return YES( BOOL ) 1; |
| 535 | } |
| 536 | |
| 537 | @end |