This is NSString+MIME.m in view mode; [Download] [Up]
//---------------------------------------------------------------------------------------
// NSString+MIME.m created by erik on Wed 08-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: NSString+MIME.m,v 1.7 1998/10/22 14:27:24 erik Exp $
//---------------------------------------------------------------------------------------
#import "Utilities.h"
#import "NSData+MIME.h"
#import "NSString+MIME.h"
//---------------------------------------------------------------------------------------
// Constants
//---------------------------------------------------------------------------------------
NSString *MIMEFormatException = @"MIMEFormatException";
NSString *MIMEAsciiStringEncoding = @"us-ascii";
NSString *MIMELatin1StringEncoding = @"iso-8859-1";
NSString *MIMELatin2StringEncoding = @"iso-8859-2";
NSString *MIME2022JPStringEncoding = @"iso-2022";
//---------------------------------------------------------------------------------------
@implementation NSString(MIMEExtensions_Alexandra)
//---------------------------------------------------------------------------------------
+ (NSString *)stringWithData:(NSData *)data MIMEEncoding:(NSString *)encodingName
{
return [[[NSString alloc] initWithData:data MIMEEncoding:encodingName] autorelease];
}
+ (NSString *)stringWithBytes:(const void *)buffer length:(unsigned int)length MIMEEncoding:(NSString *)encodingName;
{
return [self stringWithData:[NSData dataWithBytes:buffer length:length] MIMEEncoding:encodingName];
}
//---------------------------------------------------------------------------------------
// Converting to/from byte representations
//---------------------------------------------------------------------------------------
- initWithData:(NSData *)buffer MIMEEncoding:(NSString *)encodingName;
{
NSStringEncoding encoding;
encodingName = [encodingName lowercaseString];
if((encoding = [NSString stringEncodingForMIMEEncoding:encodingName]) == 0)
encoding = NSASCIIStringEncoding; // hope that NSString does something reasonable
return [self initWithData:buffer encoding:encoding];
}
- (NSData *)dataUsingMIMEEncoding:(NSString *)encodingName
{
NSStringEncoding encoding;
encodingName = [encodingName lowercaseString];
if((encoding = [NSString stringEncodingForMIMEEncoding:encodingName]) == 0)
return nil;
return [self dataUsingEncoding:encoding];
}
//---------------------------------------------------------------------------------------
// NSStringEncoding vs. MIME Encoding
//---------------------------------------------------------------------------------------
- (NSString *)recommendedMIMEEncoding;
{
if([self canBeConvertedToEncoding:NSASCIIStringEncoding])
return MIMEAsciiStringEncoding;
if([self canBeConvertedToEncoding:NSISOLatin1StringEncoding])
return MIMELatin1StringEncoding;
#ifndef LITTLE_FOUNDATION
if([self canBeConvertedToEncoding:NSISOLatin2StringEncoding])
return MIMELatin2StringEncoding;
if([self canBeConvertedToEncoding:NSISO2022JPStringEncoding])
return MIME2022JPStringEncoding;
#endif
return nil;
}
+ (NSStringEncoding)stringEncodingForMIMEEncoding:(NSString *)encoding;
{
static NSMutableDictionary *table = nil;
if(table == nil)
{
table = [NSMutableDictionary dictionary];
[table setObject:[NSNumber numberWithUnsignedInt:NSASCIIStringEncoding] forKey:MIMEAsciiStringEncoding];
[table setObject:[NSNumber numberWithUnsignedInt:NSISOLatin1StringEncoding] forKey:MIMELatin1StringEncoding];
#ifndef LITTLE_FOUNDATION
[table setObject:[NSNumber numberWithUnsignedInt:NSISOLatin2StringEncoding] forKey:MIMELatin2StringEncoding];
[table setObject:[NSNumber numberWithUnsignedInt:NSISO2022JPStringEncoding] forKey:MIME2022JPStringEncoding];
#endif
table = [table copy];
}
return [[table objectForKey:[encoding lowercaseString]] unsignedIntValue];
}
//---------------------------------------------------------------------------------------
// RFC1522 Encoding/Decoding
//---------------------------------------------------------------------------------------
- initWithMIMEHeaderFieldData:(NSData *)value;
{
NSMutableString *dest;
const char *source, *endOfSource, *vstart, *buffer;
unsigned int length;
dest = [[NSMutableString alloc] init];
buffer = [value bytes];
length = [value length];
vstart = buffer;
endOfSource = (const char *)buffer + length;
for(source = buffer; source < endOfSource; source++)
{
if((*source == EQUALS) && (source < endOfSource - 1) &&(*(source + 1) == QMARK))
{
const char *p, *q, *pmax, *qmarkp[4];
char transferEncoding;
int qmarks = 0, len;
NSStringEncoding stringEncoding;
NSString *charset, *text, *tmp;
NSData *textBuffer = nil;
pmax = source + umin(endOfSource - (char *)source, 76);
for(p=source+1; p<pmax-1 && qmarks<4; p++)
{
if(*p == QMARK)
{
if(*(p - 1) == QMARK)
break;
qmarkp[qmarks] = p;
qmarks += 1;
}
else if(*p == SPACE)
break;
}
if((qmarks < 4) || (*(qmarkp[3] + 1) != EQUALS))
{
tmp = [NSString stringWithFormat:@"MIME Decoder: '=?' didn't introducde RFC1522 word in '%@'", [NSString stringWithCString:buffer length:length]];
LOG(tmp);
continue;
}
q = qmarkp[2] + 1; len = qmarkp[3] - q;
transferEncoding = tolower(*(qmarkp[1]+1));
if(transferEncoding == 'q')
textBuffer = [[NSData dataWithBytes:(void *)q length:len] decodeQuotedPrintable1522];
else if(transferEncoding == 'b')
textBuffer = [[NSData dataWithBytes:(void *)q length:len] decodeBase64];
if(textBuffer != nil)
{
q = qmarkp[0] + 1; len = qmarkp[1] - q;
charset = [[NSString stringWithCString:q length:len] lowercaseString];
if((stringEncoding = [NSString stringEncodingForMIMEEncoding:charset]) == 0)
stringEncoding = NSASCIIStringEncoding;
text = [[NSString alloc] initWithData:textBuffer encoding:stringEncoding];
if(vstart != source)
[dest appendString:[NSString stringWithBytes:vstart length:source - vstart MIMEEncoding:MIMELatin1StringEncoding]];
[dest appendString:text];
[text release];
vstart = qmarkp[3] + 2;
source = vstart - 1; // will be incremented by loop!
}
}
}
if(vstart != source)
[dest appendString:[NSString stringWithBytes:vstart length:source - vstart
MIMEEncoding:MIMELatin1StringEncoding]];
self = [self initWithString:dest];
[dest release];
return self;
}
- (NSData *)dataForMIMEHeaderField;
{
NSCharacterSet *spaceCharacterSet;
NSMutableData *buffer;
NSScanner *scanner;
NSMutableString *text;
NSString *currentEncoding, *nextEncoding, *word, *spaces;
spaceCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@" "];
buffer = [NSMutableData data];
scanner = [NSScanner scannerWithString:self];
[scanner setCharactersToBeSkipped:nil];
text = [NSMutableString string];
currentEncoding = nil;
do
{
if([scanner scanCharactersFromSet:spaceCharacterSet intoString:&spaces] == NO)
spaces = @"";
if([scanner scanUpToCharactersFromSet:spaceCharacterSet intoString:&word] == NO)
word = nil;
nextEncoding = [word recommendedMIMEEncoding];
if((nextEncoding != currentEncoding) || ([text length] + [word length] + 7 + [currentEncoding length] > 75) || (word == nil))
{
if([text length] > 0)
{
if(currentEncoding != MIMEAsciiStringEncoding)
[buffer appendData:[text dataByWrappingInHeaderFieldWord]];
else
[buffer appendData:[text dataUsingEncoding:NSASCIIStringEncoding]];
}
[buffer appendData:[spaces dataUsingEncoding:NSASCIIStringEncoding]];
currentEncoding = nextEncoding;
text = [[word mutableCopy] autorelease];
}
else
{
[text appendString:spaces];
[text appendString:word];
}
}
while([text length] > 0);
return buffer;
}
- (NSData *)dataByWrappingInHeaderFieldWord;
{
NSMutableData *buffer;
NSData *stringData, *b64TransferData, *qpTransferData, *transferData;
NSString *characterSetName;
unsigned int b64Length, qpLength;
characterSetName = [self recommendedMIMEEncoding];
stringData = [self dataUsingMIMEEncoding:characterSetName];
b64TransferData = [stringData encodeBase64WithLineLength:UINT_MAX-3 andNewlineAtEnd:NO];
qpTransferData = [stringData encodeQuotedPrintable1522];
b64Length = [b64TransferData length]; qpLength = [qpTransferData length];
if((qpLength < 6) || (qpLength < [stringData length] * 3/2) || (qpLength <= b64Length))
transferData = qpTransferData;
else
transferData = b64TransferData;
if([transferData length] + 7 + [characterSetName length] > 75)
[NSException raise:NSInvalidArgumentException format:@"Encoding of this string results in a MIME word which exceeds the maximum length of 75 characters. Try to split it into multiple words and make sure that idividual components are separated by whitespaces."];
buffer = [NSMutableData data];
[buffer appendData:[[NSString stringWithFormat:@"=?%@?%@?", characterSetName, (transferData == qpTransferData) ? @"Q" : @"B"] dataUsingEncoding:NSASCIIStringEncoding]];
[buffer appendData:transferData];
[buffer appendData:[@"?=" dataUsingEncoding:NSASCIIStringEncoding]];
return buffer;
}
- (NSData *)dataForHeaderFieldBody;
{
return [self dataUsingMIMEEncoding:MIMEAsciiStringEncoding];
}
//---------------------------------------------------------------------------------------
@end
//---------------------------------------------------------------------------------------
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.