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.