This is MessagePart.m in view mode; [Download] [Up]
//--------------------------------------------------------------------------------------- // MessagePart.m created by erik on Mon 20-Jan-1997 // This code is part of the Alexandra Newsreader Project. For copyright details see // GNU public license version 2 or above. No warranties implied. Use at own risk. // More information can be found at <http://www.object-factory.com/Alexandra>. // @(#)$Id: MessagePart.m,v 1.8 1998/09/28 12:28:35 erik Exp $ //--------------------------------------------------------------------------------------- #import "Utilities.h" #import "Message.h" static void getHeadersAndBody(NSData *buffer, NSDictionary **headerPtr, NSData **bodyPtr); //--------------------------------------------------------------------------------------- @implementation MessagePart //--------------------------------------------------------------------------------------- + (NSString *)tempAttachmentBasename; { #ifndef LITTLE_FOUNDATION # warning implementation missing return nil; #else // NSLock missing. But then again, we're not multithreaded anyway... static int attachmentCount = 0; char buf[100]; // stringWithFormat is broken. sprintf(buf, "attachment%d", attachmentCount++); return [NSString stringWithCString:buf]; #endif } //--------------------------------------------------------------------------------------- // Factory //--------------------------------------------------------------------------------------- + (id)messagePartWithData:(NSData *)transferRep; { return [[[self alloc] initFromTransferRepresentation:transferRep] autorelease]; } //--------------------------------------------------------------------------------------- // Init //--------------------------------------------------------------------------------------- - initWithContentType:(NSString *)ct subtype:(NSString *)cst parameters:(NSDictionary *)ctp { TRACE_IN [super init]; return self; } - initFromTransferRepresentation:(NSData *)transferRep { NSData *bodyData; NSString *cteString, *string; MIMEContentType *type; MIMEContentDisposition *disposition; TRACE_IN [super init]; getHeadersAndBody(transferRep, &headerFields, &bodyData); [headerFields retain]; if((string = [self valueOfHeaderFieldNamed:@"MIME-Version"]) != nil) if([string floatValue] > 1.0) LOG(([NSString stringWithFormat:@"MIME Decoder: decoding version %@ as 1.0.", string])); if((cteString = [self valueOfHeaderFieldNamed:@"Content-Transfer-Encoding"]) != nil) cteString = [cteString stringByRemovingSurroundingWhitespace]; // i've seen this else cteString = MIME8BitContentTransferEncoding; // #?# default bodyData = [bodyData decodeContentWithTransferEncoding:cteString]; if((string = [self valueOfHeaderFieldNamed:@"Content-Type"]) != nil) type = [[[MIMEContentType alloc] initWithString:string] autorelease]; else type = [[[MIMEContentType alloc] initWithString:@"text/plain; charset=us-ascii"] autorelease]; [self setValue:type ofHeaderFieldNamed:@"Content-Type"]; if((string = [self valueOfHeaderFieldNamed:@"Content-Disposition"]) != nil) { disposition = [[[MIMEContentDisposition alloc] initWithString:string] autorelease]; [self setValue:disposition ofHeaderFieldNamed:@"Content-Disposition"]; } if([type contentType] == MIMETextContentType) [self takeTextualContentsFromData:bodyData]; else if([type contentType] == MIMEMultipartContentType) [self takeMultipartContentsFromData:bodyData]; else [self takeFileContentsFromData:bodyData]; return self; } - (void)takeTextualContentsFromData:(NSData *)data; { NSString *charset, *text; NSStringEncoding encoding; TRACE_IN if((charset = [[self contentTypeParameters] objectForKey:@"charset"]) == nil) encoding = NSASCIIStringEncoding; else if((encoding = [NSString stringEncodingForMIMEEncoding:charset]) == 0) encoding = NSASCIIStringEncoding; // hope that NSString does something reasonable text = [[[NSString alloc] initWithData:data encoding:encoding] autorelease]; [self setContents:text]; } - (void)takeFileContentsFromData:(NSData *)data; { [self setContents:data]; } - (void)takeMultipartContentsFromData:(NSData *)data; { NSMutableArray *newContents; NSString *boundary; const char *btext, *startPtr, *possibleEndPtr, *p, *pmin, *pmax, *q; unsigned int blen; NSRange subpartRange; BOOL done = NO; TRACE_IN newContents = [NSMutableArray array]; if((boundary = [[self contentTypeParameters] objectForKey:@"boundary"]) == nil) [NSException raise:MIMEFormatException format:@"no boundary for multipart"]; btext = [boundary cString]; blen = strlen(btext); pmin = p = [data bytes]; pmax = p + [data length]; startPtr = possibleEndPtr = NULL; while(done == NO) { if(p > pmax - 5 - blen) // --boundary--\n [NSException raise:MIMEFormatException format:@"final boundary not found"]; if((*p == '-') && (*(p+1) == '-') && (strncmp(p+2, btext, blen) == 0)) { q = p + 2 + blen; if((*q == '-') && (*(q+1) == '-')) { done = YES; q += 2; } if((q = skipspace(q, pmax)) == NULL) // might have been added [NSException raise:MIMEFormatException format:@"final boundary not found"]; if(iscrlf(*q) == NO) { LOG(@"ignoring junk after mime multipart boundary"); if((q = skiptonewline(q, pmax)) == NULL) [NSException raise:MIMEFormatException format:@"final boundary not found"]; } if(startPtr != NULL) { subpartRange.location = startPtr - (const char *)[data bytes]; subpartRange.length = possibleEndPtr - startPtr; [newContents addObject:[MessagePart messagePartWithData:[data subdataWithRange:subpartRange]]]; } startPtr = p = skipnewline(q, pmax); // trailing crlf belongs to boundary } else { if((p = skiptonewline(p, pmax)) == NULL) [NSException raise:MIMEFormatException format:@"final boundary not found"]; possibleEndPtr = p; p = skipnewline(p, pmax); } } [self setContents:newContents]; } //--------------------------------------------------------------------------------------- // ACCESSOR METHODS //--------------------------------------------------------------------------------------- - (NSData *)transferRepresentation; { TRACE_IN return nil; } - (NSArray *)headerFieldNames; { TRACE_IN return [headerFields allKeys]; } - (id)valueOfHeaderFieldNamed:(NSString *)aFieldName; { id object; NSString *fieldName; NSEnumerator *fieldNameEnum; TRACE_IN if((object = [headerFields objectForKey:aFieldName]) != nil) return object; fieldNameEnum = [headerFields keyEnumerator]; while(((fieldName = [fieldNameEnum nextObject]) != nil) && (object == nil)) { if([fieldName compare:aFieldName options:NSCaseInsensitiveSearch] == NSOrderedSame) object = [headerFields objectForKey:fieldName]; } return object; } - (NSString *)stringValueOfHeaderFieldNamed:(NSString *)fieldName; { id value; TRACE_IN value = [self valueOfHeaderFieldNamed:fieldName]; if([value isKindOfClass:[NSString class]]) return value; if([value isKindOfClass:[HeaderFieldBody class]]) return [value stringRepresentation]; return [value description]; } - (void)setValue:(id)object ofHeaderFieldNamed:(NSString *)aFieldName; { NSString *fieldName; NSEnumerator *fieldNameEnum; TRACE_IN if(([headerFields objectForKey:aFieldName]) == nil) { fieldNameEnum = [headerFields keyEnumerator]; while((fieldName = [fieldNameEnum nextObject]) != nil) { if([fieldName compare:aFieldName options:NSCaseInsensitiveSearch] == NSOrderedSame) [headerFields removeObjectForKey:fieldName]; } } [headerFields setObject:object forKey:aFieldName]; } - (void)setContents:(id <NSObject>)object; { TRACE_IN [contents autorelease]; contents = [object retain]; } - (id <NSObject>)contents; { TRACE_IN return contents; } //--------------------------------------------------------------------------------------- // DERIVED ACCESSOR METHODS //--------------------------------------------------------------------------------------- - (NSString *)contentType; // returns uniqued string. (compare to constant with ==) { TRACE_IN return [[self valueOfHeaderFieldNamed:@"Content-Type"] contentType]; } - (NSString *)contentSubtype; { TRACE_IN return [[self valueOfHeaderFieldNamed:@"Content-Type"] contentSubtype]; } - (NSDictionary *)contentTypeParameters; { TRACE_IN return [[self valueOfHeaderFieldNamed:@"Content-Type"] parameters]; } - (NSString *)contentDisposition; // returns uniqued string. (compare to constant with ==) { TRACE_IN return [[self valueOfHeaderFieldNamed:@"Content-Disposition"] disposition]; } - (NSDictionary *)contentDispositionParameters; { TRACE_IN return [[self valueOfHeaderFieldNamed:@"Content-Disposition"] parameters]; } - (NSString *)preferredFilename; { NSString *filename; TRACE_IN if((filename = [[self contentDispositionParameters] objectForKey:@"filename"]) != nil) return [filename lastPathComponent]; if((filename = [[self contentTypeParameters] objectForKey:@"name"]) != nil) return [filename lastPathComponent]; return nil; } - (NSString *)temporaryFilename; { // use mailcap? NSString *extension, *type, *subtype; TRACE_IN type = [self contentType]; subtype = [self contentSubtype]; if((type == MIMEApplicationContentType) && ([subtype isEqualToString:@"postscript"])) extension = @"ps"; else if((type == MIMETextContentType) && ([subtype isEqualToString:@"plain"])) extension = @"text"; else if((type == MIMEMessageContentType) && ([subtype isEqualToString:@"rfc822"])) extension = @"message"; else if([subtype hasPrefix:@"x-"]) extension = [subtype substringFromIndex:2]; else extension = subtype; #ifndef LITTLE_FOUNDATION return [[isa tempAttachmentBasename] stringByAppendingPathExtension:extension]; #else return [[[isa tempAttachmentBasename] stringByAppendingString:@"."] stringByAppendingString:extension]; #endif } - (NSString *)filename; { NSString *filename; if((filename = [self preferredFilename]) == nil) filename = [self temporaryFilename]; return filename; } //--------------------------------------------------------------------------------------- // DERIVED ACCESSOR METHODS (R/W) //--------------------------------------------------------------------------------------- - (void)setContentDescription:(NSString *)string; { TRACE_IN [self setValue:string ofHeaderFieldNamed:@"Content-Description"]; } - (NSString *)contentDescription; { TRACE_IN return [self valueOfHeaderFieldNamed:@"Content-Description"]; } - (void)setContentID:(NSString *)string; { TRACE_IN [self setValue:string ofHeaderFieldNamed:@"Content-ID"]; } - (NSString *)contentID; { TRACE_IN return [self valueOfHeaderFieldNamed:@"Content-ID"]; } //--------------------------------------------------------------------------------------- // NSObject Stuff //--------------------------------------------------------------------------------------- - (void)dealloc { TRACE_IN [headerFields release]; [contents release]; [super dealloc]; } - (NSString *)description { return [NSString stringWithFormat:@"%@ { type = %@, subtype = %@ }", [super description], [self contentType], [self contentSubtype]]; } //--------------------------------------------------------------------------------------- @end //--------------------------------------------------------------------------------------- static void getHeadersAndBody(NSData *buffer, NSDictionary **headerPtr, NSData **bodyPtr) { const char *p, *pmax, *fnamePtr, *fbodyPtr, *eolPtr; NSMutableDictionary *headerFields; NSMutableData *fbodyData; NSString *name, *content; NSRange bodyRange; headerFields = [NSMutableDictionary dictionary]; name = nil; fnamePtr = p = [buffer bytes]; pmax = p + [buffer length]; fbodyPtr = NULL; fbodyData = nil; for(;p < pmax; p++) { if((*p == COLON) && (fbodyPtr == NULL)) { fbodyPtr = p + 1; if((fbodyPtr < pmax) && (iswhitespace(*fbodyPtr))) fbodyPtr += 1; name = [NSString stringWithCString:fnamePtr length:(p - fnamePtr)]; } else if(iscrlf(*p)) { eolPtr = p; p = skipnewline(p, pmax); if((p < pmax) && iswhitespace(*p)) // folded header! { if(fbodyData == nil) fbodyData = [NSMutableData dataWithBytes:fbodyPtr length:(eolPtr - fbodyPtr)]; else [fbodyData appendBytes:fbodyPtr length:(eolPtr - fbodyPtr)]; fbodyPtr = p; } else { if(fbodyData == nil) fbodyData = (id)[NSData dataWithBytes:fbodyPtr length:(eolPtr - fbodyPtr)]; else [fbodyData appendBytes:fbodyPtr length:(eolPtr - fbodyPtr)]; content = [[[NSString alloc] initWithMIMEHeaderFieldData:fbodyData] autorelease]; [headerFields setObject:content forKey:name]; fbodyData = nil; fnamePtr = p; fbodyPtr = NULL; if((p < pmax) && iscrlf(*p)) break; } } } *headerPtr = headerFields; p = skipnewline(p, pmax); bodyRange = NSMakeRange(p - (char *)[buffer bytes], pmax - p); *bodyPtr = [buffer subdataWithRange:bodyRange]; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.