mirror of
https://github.com/gnustep/libs-base.git
synced 2025-04-23 00:41:02 +00:00
Tidied decoding of data.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@8142 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
13a505f5ad
commit
999cd23cc9
4 changed files with 565 additions and 346 deletions
|
@ -31,6 +31,28 @@
|
|||
</desc>
|
||||
</method>
|
||||
|
||||
<method type="GSMimeCodingContext*">
|
||||
<sel>contextFor:</sel>
|
||||
<arg type="NSDictionary*">headerInfo</arg>
|
||||
<desc>
|
||||
Return a coding context object to be used for decoding data
|
||||
according to the scheme specified in the header.
|
||||
<p>
|
||||
The default implementation supports the following transfer
|
||||
encodings specified in either a <code>transfer-encoding</code>
|
||||
of <code>content-transfer-encoding</code> header -
|
||||
</p>
|
||||
<list>
|
||||
<item>base64</item>
|
||||
<item>quoted-printable</item>
|
||||
<item>binary</item>
|
||||
<item>7bit</item>
|
||||
<item>8bit</item>
|
||||
<item>chunked (for HTTP/1.1)</item>
|
||||
</list>
|
||||
</desc>
|
||||
</method>
|
||||
|
||||
<method type="BOOL">
|
||||
<sel>decodeData:</sel>
|
||||
<arg type="NSData*">sourceData</arg>
|
||||
|
@ -39,7 +61,7 @@
|
|||
<sel>intoData:</sel>
|
||||
<arg type="NSMutableData*">destinationData</arg>
|
||||
<sel>withContext:</sel>
|
||||
<arg type="GSMimeEncodingContext*">ctxt</arg>
|
||||
<arg type="GSMimeCodingContext*">ctxt</arg>
|
||||
<desc>
|
||||
<p>
|
||||
Decodes the raw data from the specified range in the source
|
||||
|
|
|
@ -34,15 +34,16 @@
|
|||
<h2>Methods </h2>
|
||||
<ul>
|
||||
<li><a href ="GSMimeParser.html#method-0">+mimeParser</a>
|
||||
<li><a href ="GSMimeParser.html#method-1">-decodeData:fromRange:intoData:withContext:</a>
|
||||
<li><a href ="GSMimeParser.html#method-2">-document</a>
|
||||
<li><a href ="GSMimeParser.html#method-3">-parse:</a>
|
||||
<li><a href ="GSMimeParser.html#method-4">-parseHeader:</a>
|
||||
<li><a href ="GSMimeParser.html#method-5">-parsingHeaders</a>
|
||||
<li><a href ="GSMimeParser.html#method-6">-scanHeader:named:inTo:</a>
|
||||
<li><a href ="GSMimeParser.html#method-7">-scanSpace:</a>
|
||||
<li><a href ="GSMimeParser.html#method-8">-scanSpecial:</a>
|
||||
<li><a href ="GSMimeParser.html#method-9">-scanToken:</a>
|
||||
<li><a href ="GSMimeParser.html#method-1">-contextFor:</a>
|
||||
<li><a href ="GSMimeParser.html#method-2">-decodeData:fromRange:intoData:withContext:</a>
|
||||
<li><a href ="GSMimeParser.html#method-3">-document</a>
|
||||
<li><a href ="GSMimeParser.html#method-4">-parse:</a>
|
||||
<li><a href ="GSMimeParser.html#method-5">-parseHeader:</a>
|
||||
<li><a href ="GSMimeParser.html#method-6">-parsingHeaders</a>
|
||||
<li><a href ="GSMimeParser.html#method-7">-scanHeader:named:inTo:</a>
|
||||
<li><a href ="GSMimeParser.html#method-8">-scanSpace:</a>
|
||||
<li><a href ="GSMimeParser.html#method-9">-scanSpecial:</a>
|
||||
<li><a href ="GSMimeParser.html#method-10">-scanToken:</a>
|
||||
</ul>
|
||||
<hr><h2>Class Methods </h2>
|
||||
<h3><a name ="method-0">mimeParser</a></h3>
|
||||
|
@ -52,8 +53,31 @@
|
|||
|
||||
<hr>
|
||||
<hr><h2>Instances Methods </h2>
|
||||
<h3><a name ="method-1">decodeData:fromRange:intoData:withContext:</a></h3>
|
||||
- (BOOL) <b>decodeData:</b> (NSData*)sourceData <b>fromRange:</b> (NSRange)aRange <b>intoData:</b> (NSMutableData*)destinationData <b>withContext:</b> (GSMimeEncodingContext*)ctxt;<br>
|
||||
<h3><a name ="method-1">contextFor:</a></h3>
|
||||
- (GSMimeCodingContext*) <b>contextFor:</b> (NSDictionary*)headerInfo;<br>
|
||||
|
||||
Return a coding context object to be used for decoding data
|
||||
according to the scheme specified in the header.
|
||||
<p>
|
||||
|
||||
The default implementation supports the following transfer
|
||||
encodings specified in either a <code>transfer-encoding</code>
|
||||
of <code>content-transfer-encoding</code> header -
|
||||
</p>
|
||||
|
||||
<ul>
|
||||
<li>base64
|
||||
<li>quoted-printable
|
||||
<li>binary
|
||||
<li>7bit
|
||||
<li>8bit
|
||||
<li>chunked (for HTTP/1.1)
|
||||
</ul>
|
||||
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-2">decodeData:fromRange:intoData:withContext:</a></h3>
|
||||
- (BOOL) <b>decodeData:</b> (NSData*)sourceData <b>fromRange:</b> (NSRange)aRange <b>intoData:</b> (NSMutableData*)destinationData <b>withContext:</b> (GSMimeCodingContext*)ctxt;<br>
|
||||
|
||||
<p>
|
||||
|
||||
|
@ -81,13 +105,13 @@
|
|||
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-2">document</a></h3>
|
||||
<h3><a name ="method-3">document</a></h3>
|
||||
- (GSMimeDocument*) <b>document</b>;<br>
|
||||
|
||||
Returns the object into which raw mime data is being parsed.
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-3">parse:</a></h3>
|
||||
<h3><a name ="method-4">parse:</a></h3>
|
||||
- (BOOL) <b>parse:</b> (NSData*)rawData;<br>
|
||||
|
||||
This method is called repeatedly to pass raw mime data into
|
||||
|
@ -96,7 +120,7 @@
|
|||
all the available information.
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-4">parseHeader:</a></h3>
|
||||
<h3><a name ="method-5">parseHeader:</a></h3>
|
||||
- (BOOL) <b>parseHeader:</b> (NSString*)aRawHeader;<br>
|
||||
|
||||
<p>
|
||||
|
@ -144,7 +168,7 @@
|
|||
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-5">parsingHeaders</a></h3>
|
||||
<h3><a name ="method-6">parsingHeaders</a></h3>
|
||||
- (BOOL) <b>parsingHeaders</b>;<br>
|
||||
|
||||
Returns YES if the parser is expecting to read mime headers,
|
||||
|
@ -153,7 +177,7 @@
|
|||
the mime message (or has been passed all data).
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-6">scanHeader:named:inTo:</a></h3>
|
||||
<h3><a name ="method-7">scanHeader:named:inTo:</a></h3>
|
||||
- (BOOL) <b>scanHeader:</b> (NSScanner*)aScanner <b>named:</b> (NSString*)aName <b>inTo:</b> (NSMutableDictionary*)info;<br>
|
||||
|
||||
<p>
|
||||
|
@ -260,7 +284,7 @@
|
|||
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-7">scanSpace:</a></h3>
|
||||
<h3><a name ="method-8">scanSpace:</a></h3>
|
||||
- (BOOL) <b>scanSpace:</b> (NSScanner*)aScanner;<br>
|
||||
|
||||
A convenience method to scan past any whitespace in the scanner
|
||||
|
@ -268,7 +292,7 @@
|
|||
comes after it. Returns YES if any space was read, NO otherwise.
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-8">scanSpecial:</a></h3>
|
||||
<h3><a name ="method-9">scanSpecial:</a></h3>
|
||||
- (NSString*) <b>scanSpecial:</b> (NSScanner*)aScanner;<br>
|
||||
|
||||
A convenience method to use a scanner (that is set up to scan a
|
||||
|
@ -278,7 +302,7 @@
|
|||
will contain a single space character.
|
||||
|
||||
<hr>
|
||||
<h3><a name ="method-9">scanToken:</a></h3>
|
||||
<h3><a name ="method-10">scanToken:</a></h3>
|
||||
- (NSString*) <b>scanToken:</b> (NSScanner*)aScanner;<br>
|
||||
|
||||
A convenience method to use a scanner (that is set up to scan a
|
||||
|
|
|
@ -39,28 +39,16 @@
|
|||
@class NSString;
|
||||
@class NSMutableString;
|
||||
|
||||
typedef enum {
|
||||
GSMimeEncodingBase64,
|
||||
GSMimeEncodingQuotedPrintable,
|
||||
GSMimeEncodingSevenBit,
|
||||
GSMimeEncodingEightBit,
|
||||
GSMimeEncodingBinary,
|
||||
GSMimeEncodingChunked, // HTTP/1.1 chunked transfer
|
||||
GSMimeEncodingUnknown
|
||||
} GSMimeEncoding;
|
||||
|
||||
/*
|
||||
* A trivial class for mantaining state while decoding/encoding data.
|
||||
* Each encoding type requires its own subclass.
|
||||
*/
|
||||
@interface GSMimeEncodingContext : NSObject
|
||||
@interface GSMimeCodingContext : NSObject
|
||||
{
|
||||
@public
|
||||
GSMimeEncoding type; /* The encoding type to be used. */
|
||||
unsigned char buf[8]; /* Temporary data storage area. */
|
||||
int pos; /* Context position count. */
|
||||
BOOL foot; /* Reading footer near end of data. */
|
||||
BOOL atEnd; /* Flag to say that data has ended. */
|
||||
BOOL atEnd; /* Flag to say that data has ended. */
|
||||
}
|
||||
- (BOOL) atEnd;
|
||||
- (void) setAtEnd: (BOOL)flag;
|
||||
@end
|
||||
|
||||
@interface GSMimeDocument : NSObject
|
||||
|
@ -96,15 +84,16 @@ typedef enum {
|
|||
NSData *boundary;
|
||||
GSMimeDocument *document;
|
||||
GSMimeParser *child;
|
||||
GSMimeEncodingContext *context;
|
||||
GSMimeCodingContext *context;
|
||||
}
|
||||
|
||||
+ (GSMimeParser*) mimeParser;
|
||||
|
||||
- (GSMimeCodingContext*) contextFor: (NSDictionary*)headerInfo;
|
||||
- (BOOL) decodeData: (NSData*)sData
|
||||
fromRange: (NSRange)aRange
|
||||
intoData: (NSMutableData*)dData
|
||||
withContext: (GSMimeEncodingContext*)ctxt;
|
||||
withContext: (GSMimeCodingContext*)ctxt;
|
||||
- (GSMimeDocument*) document;
|
||||
- (BOOL) parse: (NSData*)input;
|
||||
- (BOOL) parseHeader: (NSString*)aRawHeader;
|
||||
|
|
798
Source/GSMime.m
798
Source/GSMime.m
|
@ -189,9 +189,105 @@ parseCharacterSet(NSString *token)
|
|||
return NSASCIIStringEncoding;
|
||||
}
|
||||
|
||||
@implementation GSMimeEncodingContext
|
||||
@implementation GSMimeCodingContext
|
||||
- (BOOL) atEnd
|
||||
{
|
||||
return atEnd;
|
||||
}
|
||||
|
||||
- (id) copyWithZone: (NSZone*)z
|
||||
{
|
||||
return RETAIN(self);
|
||||
}
|
||||
|
||||
- (void) setAtEnd: (BOOL)flag
|
||||
{
|
||||
atEnd = flag;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface GSMimeBase64DecoderContext : GSMimeCodingContext
|
||||
{
|
||||
@public
|
||||
unsigned char buf[4];
|
||||
unsigned pos;
|
||||
}
|
||||
@end
|
||||
@implementation GSMimeBase64DecoderContext
|
||||
@end
|
||||
|
||||
@interface GSMimeQuotedDecoderContext : GSMimeCodingContext
|
||||
{
|
||||
@public
|
||||
unsigned char buf[4];
|
||||
unsigned pos;
|
||||
}
|
||||
@end
|
||||
@implementation GSMimeQuotedDecoderContext
|
||||
@end
|
||||
|
||||
@interface GSMimeChunkedDecoderContext : GSMimeCodingContext
|
||||
{
|
||||
@public
|
||||
unsigned char buf[8];
|
||||
unsigned pos;
|
||||
enum {
|
||||
ChunkSize, // Reading chunk size
|
||||
ChunkExt, // Reading cjhunk extensions
|
||||
ChunkEol1, // Reading end of line after size;ext
|
||||
ChunkData, // Reading chunk data
|
||||
ChunkEol2, // Reading end of line after data
|
||||
ChunkFoot, // Reading chunk footer after newline
|
||||
ChunkFootA // Reading chunk footer
|
||||
} state;
|
||||
NSMutableData *data;
|
||||
}
|
||||
@end
|
||||
@implementation GSMimeChunkedDecoderContext
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(data);
|
||||
[super dealloc];
|
||||
}
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
if (self != nil)
|
||||
{
|
||||
data = [NSMutableData new];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface GSMimeBinaryDecoderContext : GSMimeCodingContext
|
||||
@end
|
||||
@implementation GSMimeBinaryDecoderContext
|
||||
- (id) autorelease
|
||||
{
|
||||
return self;
|
||||
}
|
||||
- (id) copyWithZone: (NSZone*)z
|
||||
{
|
||||
return self;
|
||||
}
|
||||
- (void) dealloc
|
||||
{
|
||||
NSLog(@"Error - attempt to deallocate GSMimeBinaryDecoderContext");
|
||||
}
|
||||
- (id) retain
|
||||
{
|
||||
return self;
|
||||
}
|
||||
- (void) release
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
@interface GSMimeParser (Private)
|
||||
- (BOOL) _decodeBody: (NSData*)data;
|
||||
|
@ -216,10 +312,66 @@ parseCharacterSet(NSString *token)
|
|||
[super dealloc];
|
||||
}
|
||||
|
||||
- (GSMimeCodingContext*) contextFor: (NSDictionary*)info
|
||||
{
|
||||
NSString *name;
|
||||
NSString *value;
|
||||
static GSMimeCodingContext *defaultContext = nil;
|
||||
|
||||
if (defaultContext == nil)
|
||||
{
|
||||
defaultContext = [GSMimeBinaryDecoderContext new];
|
||||
}
|
||||
if (info == nil)
|
||||
{
|
||||
NSLog(@"contextFor: - nil header ... assumed binary encoding");
|
||||
return defaultContext;
|
||||
}
|
||||
|
||||
name = [info objectForKey: @"Name"];
|
||||
if ([name isEqualToString: @"content-transfer-encoding"] == YES
|
||||
|| [name isEqualToString: @"transfer-encoding"] == YES)
|
||||
{
|
||||
value = [info objectForKey: @"Value"];
|
||||
if ([value length] == 0)
|
||||
{
|
||||
NSLog(@"Bad value for %@ header - assume binary encoding", name);
|
||||
return defaultContext;
|
||||
}
|
||||
if ([value isEqualToString: @"base64"] == YES)
|
||||
{
|
||||
return AUTORELEASE([GSMimeBase64DecoderContext new]);
|
||||
}
|
||||
else if ([value isEqualToString: @"quoted-printable"] == YES)
|
||||
{
|
||||
return AUTORELEASE([GSMimeQuotedDecoderContext new]);
|
||||
}
|
||||
else if ([value isEqualToString: @"binary"] == YES)
|
||||
{
|
||||
return defaultContext;
|
||||
}
|
||||
else if ([value characterAtIndex: 0] == '7')
|
||||
{
|
||||
return defaultContext;
|
||||
}
|
||||
else if ([value characterAtIndex: 0] == '8')
|
||||
{
|
||||
return defaultContext;
|
||||
}
|
||||
else if ([value isEqualToString: @"chunked"] == YES)
|
||||
{
|
||||
return AUTORELEASE([GSMimeChunkedDecoderContext new]);
|
||||
}
|
||||
}
|
||||
|
||||
NSLog(@"contextFor: - unknown header (%@) ... assumed binary encoding", name);
|
||||
return defaultContext;
|
||||
}
|
||||
|
||||
- (BOOL) decodeData: (NSData*)sData
|
||||
fromRange: (NSRange)aRange
|
||||
intoData: (NSMutableData*)dData
|
||||
withContext: (GSMimeEncodingContext*)ctxt
|
||||
withContext: (GSMimeCodingContext*)con
|
||||
{
|
||||
unsigned size = [dData length];
|
||||
unsigned len = [sData length];
|
||||
|
@ -227,22 +379,15 @@ parseCharacterSet(NSString *token)
|
|||
unsigned char *dst;
|
||||
const char *src;
|
||||
const char *end;
|
||||
Class ccls;
|
||||
|
||||
if (dData == nil || ctxt == nil)
|
||||
if (dData == nil || [con isKindOfClass: [GSMimeCodingContext class]] == NO)
|
||||
{
|
||||
[NSException raise: NSInvalidArgumentException
|
||||
format: @"Bad data or context"];
|
||||
format: @"Bad destination data for decode"];
|
||||
}
|
||||
GS_RANGE_CHECK(aRange, len);
|
||||
|
||||
/*
|
||||
* A nil data item as input represents end of data.
|
||||
*/
|
||||
if (sData == nil)
|
||||
{
|
||||
ctxt->atEnd = YES;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get pointers into source data buffer.
|
||||
*/
|
||||
|
@ -250,278 +395,353 @@ parseCharacterSet(NSString *token)
|
|||
src += aRange.location;
|
||||
end = src + aRange.length;
|
||||
|
||||
switch (ctxt->type)
|
||||
ccls = [con class];
|
||||
if (ccls == [GSMimeBase64DecoderContext class])
|
||||
{
|
||||
case GSMimeEncodingBase64:
|
||||
/*
|
||||
* Expand destination data buffer to have capacity to handle info.
|
||||
*/
|
||||
[dData setLength: size + (3 * (end + 8 - src))/4];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
beg = dst;
|
||||
GSMimeBase64DecoderContext *ctxt;
|
||||
|
||||
/*
|
||||
* Now decode data into buffer, keeping count and temporary
|
||||
* data in context.
|
||||
*/
|
||||
while (src < end)
|
||||
{
|
||||
int cc = *src++;
|
||||
ctxt = (GSMimeBase64DecoderContext*)con;
|
||||
|
||||
if (isupper(cc))
|
||||
{
|
||||
cc -= 'A';
|
||||
}
|
||||
else if (islower(cc))
|
||||
{
|
||||
cc = cc - 'a' + 26;
|
||||
}
|
||||
else if (isdigit(cc))
|
||||
{
|
||||
cc = cc - '0' + 52;
|
||||
}
|
||||
else if (cc == '+')
|
||||
{
|
||||
cc = 62;
|
||||
}
|
||||
else if (cc == '/')
|
||||
{
|
||||
cc = 63;
|
||||
}
|
||||
else if (cc == '=')
|
||||
{
|
||||
ctxt->atEnd = YES;
|
||||
cc = -1;
|
||||
}
|
||||
else if (cc == '-')
|
||||
{
|
||||
ctxt->atEnd = YES;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cc = -1; /* ignore */
|
||||
}
|
||||
/*
|
||||
* Expand destination data buffer to have capacity to handle info.
|
||||
*/
|
||||
[dData setLength: size + (3 * (end + 8 - src))/4];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
beg = dst;
|
||||
|
||||
if (cc >= 0)
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = cc;
|
||||
if (ctxt->pos == 4)
|
||||
/*
|
||||
* Now decode data into buffer, keeping count and temporary
|
||||
* data in context.
|
||||
*/
|
||||
while (src < end)
|
||||
{
|
||||
int cc = *src++;
|
||||
|
||||
if (isupper(cc))
|
||||
{
|
||||
cc -= 'A';
|
||||
}
|
||||
else if (islower(cc))
|
||||
{
|
||||
cc = cc - 'a' + 26;
|
||||
}
|
||||
else if (isdigit(cc))
|
||||
{
|
||||
cc = cc - '0' + 52;
|
||||
}
|
||||
else if (cc == '+')
|
||||
{
|
||||
cc = 62;
|
||||
}
|
||||
else if (cc == '/')
|
||||
{
|
||||
cc = 63;
|
||||
}
|
||||
else if (cc == '=')
|
||||
{
|
||||
[ctxt setAtEnd: YES];
|
||||
cc = -1;
|
||||
}
|
||||
else if (cc == '-')
|
||||
{
|
||||
[ctxt setAtEnd: YES];
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
cc = -1; /* ignore */
|
||||
}
|
||||
|
||||
if (cc >= 0)
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = cc;
|
||||
if (ctxt->pos == 4)
|
||||
{
|
||||
ctxt->pos = 0;
|
||||
decodebase64(dst, ctxt->buf);
|
||||
dst += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Odd characters at end of decoded data need to be added separately.
|
||||
*/
|
||||
if ([ctxt atEnd] == YES && ctxt->pos > 0)
|
||||
{
|
||||
unsigned len = ctxt->pos - 1;;
|
||||
|
||||
while (ctxt->pos < 4)
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = '\0';
|
||||
}
|
||||
ctxt->pos = 0;
|
||||
decodebase64(dst, ctxt->buf);
|
||||
size += len;
|
||||
}
|
||||
[dData setLength: size + dst - beg];
|
||||
}
|
||||
else if (ccls == [GSMimeQuotedDecoderContext class])
|
||||
{
|
||||
GSMimeQuotedDecoderContext *ctxt;
|
||||
|
||||
ctxt = (GSMimeQuotedDecoderContext*)con;
|
||||
|
||||
/*
|
||||
* Expand destination data buffer to have capacity to handle info.
|
||||
*/
|
||||
[dData setLength: size + (end - src)];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
beg = dst;
|
||||
|
||||
while (src < end)
|
||||
{
|
||||
if (ctxt->pos > 0)
|
||||
{
|
||||
if ((*src == '\n') || (*src == '\r'))
|
||||
{
|
||||
ctxt->pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = '=';
|
||||
if (ctxt->pos == 3)
|
||||
{
|
||||
int c;
|
||||
int val;
|
||||
|
||||
ctxt->pos = 0;
|
||||
c = ctxt->buf[1];
|
||||
val = isdigit(c) ? (c - '0') : (c - 55);
|
||||
val *= 0x10;
|
||||
c = ctxt->buf[2];
|
||||
val += isdigit(c) ? (c - '0') : (c - 55);
|
||||
*dst++ = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (*src == '=')
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = '=';
|
||||
}
|
||||
else
|
||||
{
|
||||
*dst++ = *src;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
[dData setLength: size + dst - beg];
|
||||
}
|
||||
else if (ccls == [GSMimeChunkedDecoderContext class])
|
||||
{
|
||||
GSMimeChunkedDecoderContext *ctxt;
|
||||
const char *footers = src;
|
||||
|
||||
ctxt = (GSMimeChunkedDecoderContext*)con;
|
||||
|
||||
dst = beg = 0;
|
||||
while ([ctxt atEnd] == NO && src < end)
|
||||
{
|
||||
switch (ctxt->state)
|
||||
{
|
||||
case ChunkSize:
|
||||
if (isxdigit(*src) && ctxt->pos < sizeof(ctxt->buf))
|
||||
{
|
||||
ctxt->pos = 0;
|
||||
decodebase64(dst, ctxt->buf);
|
||||
dst += 3;
|
||||
ctxt->buf[ctxt->pos++] = *src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Odd characters at end of decoded data need to be added separately.
|
||||
*/
|
||||
if (ctxt->atEnd == YES && ctxt->pos > 0)
|
||||
{
|
||||
unsigned len = ctxt->pos - 1;;
|
||||
|
||||
while (ctxt->pos < 4)
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = '\0';
|
||||
}
|
||||
ctxt->pos = 0;
|
||||
decodebase64(dst, ctxt->buf);
|
||||
size += len;
|
||||
}
|
||||
[dData setLength: size + dst - beg];
|
||||
break;
|
||||
|
||||
case GSMimeEncodingQuotedPrintable:
|
||||
/*
|
||||
* Expand destination data buffer to have capacity to handle info.
|
||||
*/
|
||||
[dData setLength: size + (end - src)];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
beg = dst;
|
||||
|
||||
while (src < end)
|
||||
{
|
||||
if (ctxt->pos > 0)
|
||||
{
|
||||
if ((*src == '\n') || (*src == '\r'))
|
||||
else if (*src == ';')
|
||||
{
|
||||
ctxt->pos = 0;
|
||||
ctxt->state = ChunkExt;
|
||||
}
|
||||
else
|
||||
else if (*src == '\r')
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = '=';
|
||||
if (ctxt->pos == 3)
|
||||
{
|
||||
int c;
|
||||
int val;
|
||||
|
||||
ctxt->pos = 0;
|
||||
c = ctxt->buf[1];
|
||||
val = isdigit(c) ? (c - '0') : (c - 55);
|
||||
val *= 0x10;
|
||||
c = ctxt->buf[2];
|
||||
val += isdigit(c) ? (c - '0') : (c - 55);
|
||||
*dst++ = val;
|
||||
}
|
||||
ctxt->state = ChunkEol1;
|
||||
}
|
||||
}
|
||||
else if (*src == '=')
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = '=';
|
||||
}
|
||||
else
|
||||
{
|
||||
*dst++ = *src;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
[dData setLength: size + dst - beg];
|
||||
break;
|
||||
|
||||
case GSMimeEncodingChunked:
|
||||
while (ctxt->atEnd == NO && src < end)
|
||||
{
|
||||
/*
|
||||
* If we are reading a chunk footer, look for a blank line
|
||||
* that terminates it.
|
||||
*/
|
||||
if (ctxt->foot == YES)
|
||||
{
|
||||
if (*src == '\r')
|
||||
else if (*src == '\n')
|
||||
{
|
||||
src++;
|
||||
ctxt->state = ChunkData;
|
||||
}
|
||||
else if (*src != '\n' || ctxt->buf[0] != '\n')
|
||||
src++;
|
||||
if (ctxt->state != ChunkSize)
|
||||
{
|
||||
ctxt->buf[0] = *src++;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctxt->foot = NO;
|
||||
ctxt->atEnd = YES;
|
||||
src++;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Keep track of chunk size in the context.
|
||||
* A negative 'pos' indicates that we are reading the chunk size.
|
||||
* A positive 'pos' indicates that we are reading the chunk itsself.
|
||||
*/
|
||||
if (ctxt->pos <= 0)
|
||||
{
|
||||
BOOL foundChunkSize = NO;
|
||||
|
||||
/*
|
||||
* If we have a negative 'pos', convert it to a positive
|
||||
* count of bytes read for the chunk size.
|
||||
*/
|
||||
if (ctxt->pos < 0)
|
||||
{
|
||||
ctxt->pos = -ctxt->pos;
|
||||
}
|
||||
while (src < end)
|
||||
{
|
||||
if (isxdigit(*src))
|
||||
{
|
||||
ctxt->buf[ctxt->pos++] = *src++;
|
||||
if (ctxt->pos == sizeof(ctxt->buf))
|
||||
{
|
||||
NSLog(@"Bad chunk size field");
|
||||
ctxt->pos = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*src == '\r')
|
||||
{
|
||||
src++;
|
||||
}
|
||||
if (*src == '\n')
|
||||
{
|
||||
src++;
|
||||
}
|
||||
foundChunkSize = YES;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundChunkSize == YES)
|
||||
{
|
||||
int size = 0;
|
||||
int val = 0;
|
||||
int index;
|
||||
|
||||
for (index = 0; index < ctxt->pos; index++)
|
||||
{
|
||||
size *= 16;
|
||||
val *= 16;
|
||||
if (isdigit(ctxt->buf[index]))
|
||||
{
|
||||
size += ctxt->buf[index] - '0';
|
||||
val += ctxt->buf[index] - '0';
|
||||
}
|
||||
else if (isupper(ctxt->buf[index]))
|
||||
{
|
||||
size += ctxt->buf[index] - 'A' + 10;
|
||||
val += ctxt->buf[index] - 'A' + 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
size += ctxt->buf[index] - 'a' + 10;
|
||||
val += ctxt->buf[index] - 'a' + 10;
|
||||
}
|
||||
}
|
||||
ctxt->pos = size;
|
||||
ctxt->pos = val;
|
||||
/*
|
||||
* Check to see if this is the terminator for the
|
||||
* document content.
|
||||
* If we have read a chunk already, make sure that our
|
||||
* destination size is updated correctly before growing
|
||||
* the buffer for another chunk.
|
||||
*/
|
||||
if (ctxt->pos == 0)
|
||||
{
|
||||
ctxt->foot = YES;
|
||||
ctxt->buf[0] = src[-1]; // last char read
|
||||
}
|
||||
size += (dst - beg);
|
||||
[dData setLength: size + val];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
dst += size;
|
||||
beg = dst;
|
||||
}
|
||||
else
|
||||
{
|
||||
ctxt->pos = -ctxt->pos;
|
||||
}
|
||||
}
|
||||
if (ctxt->pos > 0)
|
||||
{
|
||||
/*
|
||||
* Expand destination data buffer to have capacity to
|
||||
* handle remainder of chunk.
|
||||
*/
|
||||
[dData setLength: size + ctxt->pos];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
beg = dst;
|
||||
/*
|
||||
* Read the specified chunk length (excluding carriage returns)
|
||||
*/
|
||||
while (ctxt->pos > 0 && src < end)
|
||||
{
|
||||
if (*src != '\r')
|
||||
{
|
||||
*dst++ = *src;
|
||||
ctxt->pos--;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
[dData setLength: size + dst - beg];
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
default:
|
||||
NSLog(@"Content encoding %d not known - assume binary", ctxt->type);
|
||||
case GSMimeEncodingBinary:
|
||||
case GSMimeEncodingSevenBit:
|
||||
case GSMimeEncodingEightBit:
|
||||
[dData setLength: size + (end - src)];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
memcpy(&dst[size], src, (end - src));
|
||||
[dData setLength: size + end - src];
|
||||
break;
|
||||
case ChunkExt:
|
||||
if (*src == '\r')
|
||||
{
|
||||
ctxt->state = ChunkEol1;
|
||||
}
|
||||
else if (*src == '\n')
|
||||
{
|
||||
ctxt->state = ChunkData;
|
||||
}
|
||||
src++;
|
||||
break;
|
||||
|
||||
case ChunkEol1:
|
||||
if (*src == '\n')
|
||||
{
|
||||
ctxt->state = ChunkData;
|
||||
}
|
||||
src++;
|
||||
break;
|
||||
|
||||
case ChunkData:
|
||||
/*
|
||||
* If the pos is non-zero, we have a data chunk to read.
|
||||
* otherwise, what we actually want it to read footers.
|
||||
*/
|
||||
if (ctxt->pos > 0)
|
||||
{
|
||||
*dst++ = *src++;
|
||||
if (--ctxt->pos == 0)
|
||||
{
|
||||
ctxt->state = ChunkEol2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
footers = src; // Record start position.
|
||||
ctxt->state = ChunkFoot;
|
||||
}
|
||||
break;
|
||||
|
||||
case ChunkEol2:
|
||||
if (*src == '\n')
|
||||
{
|
||||
ctxt->state = ChunkSize;
|
||||
}
|
||||
src++;
|
||||
break;
|
||||
|
||||
case ChunkFoot:
|
||||
if (*src == '\r')
|
||||
{
|
||||
src++;
|
||||
}
|
||||
else if (*src == '\n')
|
||||
{
|
||||
[ctxt setAtEnd: YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
ctxt->state = ChunkFootA;
|
||||
}
|
||||
break;
|
||||
|
||||
case ChunkFootA:
|
||||
if (*src == '\n')
|
||||
{
|
||||
ctxt->state = ChunkFootA;
|
||||
}
|
||||
src++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ctxt->state == ChunkFoot || ctxt->state == ChunkFootA)
|
||||
{
|
||||
[ctxt->data appendBytes: footers length: src - footers];
|
||||
if ([ctxt atEnd] == YES)
|
||||
{
|
||||
NSMutableData *old;
|
||||
|
||||
/*
|
||||
* Pretend we are back parsing the original headers ...
|
||||
*/
|
||||
old = data;
|
||||
data = ctxt->data;
|
||||
bytes = (unsigned char*)[data mutableBytes];
|
||||
dataEnd = [data length];
|
||||
inBody = NO;
|
||||
|
||||
/*
|
||||
* Duplicate the normal header parsing process for our footers.
|
||||
*/
|
||||
while (inBody == NO)
|
||||
{
|
||||
if ([self _unfoldHeader] == NO)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (inBody == NO)
|
||||
{
|
||||
NSString *header;
|
||||
|
||||
header = [self _decodeHeader];
|
||||
if (header == nil)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if ([self parseHeader: header] == NO)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* restore original data.
|
||||
*/
|
||||
ctxt->data = data;
|
||||
data = old;
|
||||
bytes = (unsigned char*)[data mutableBytes];
|
||||
dataEnd = [data length];
|
||||
inBody = YES;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Append any data.
|
||||
*/
|
||||
[dData setLength: size + dst - beg];
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Assume binary (no) decoding required.
|
||||
*/
|
||||
[dData setLength: size + (end - src)];
|
||||
dst = (unsigned char*)[dData mutableBytes];
|
||||
memcpy(&dst[size], src, (end - src));
|
||||
[dData setLength: size + end - src];
|
||||
}
|
||||
|
||||
/*
|
||||
* A nil data item as input represents end of data.
|
||||
*/
|
||||
if (sData == nil)
|
||||
{
|
||||
[con setAtEnd: YES];
|
||||
}
|
||||
|
||||
return YES;
|
||||
|
@ -548,7 +768,7 @@ parseCharacterSet(NSString *token)
|
|||
{
|
||||
data = [[NSMutableData alloc] init];
|
||||
document = [[GSMimeDocument alloc] init];
|
||||
context = [[GSMimeEncodingContext alloc] init];
|
||||
context = [[GSMimeCodingContext alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
@ -710,55 +930,6 @@ parseCharacterSet(NSString *token)
|
|||
}
|
||||
[document deleteHeaderNamed: name]; // Should be unique
|
||||
}
|
||||
else if ([name isEqualToString: @"content-transfer-encoding"] == YES
|
||||
|| [name isEqualToString: @"transfer-encoding"] == YES)
|
||||
{
|
||||
BOOL supported = NO;
|
||||
|
||||
value = [info objectForKey: @"Value"];
|
||||
if ([value length] == 0)
|
||||
{
|
||||
NSLog(@"Bad value for %@ header", name);
|
||||
return NO;
|
||||
}
|
||||
if ([value isEqualToString: @"quoted-printable"] == YES)
|
||||
{
|
||||
context->type = GSMimeEncodingQuotedPrintable;
|
||||
supported = YES;
|
||||
}
|
||||
else if ([value isEqualToString: @"base64"] == YES)
|
||||
{
|
||||
context->type = GSMimeEncodingBase64;
|
||||
supported = YES;
|
||||
}
|
||||
else if ([value isEqualToString: @"binary"] == YES)
|
||||
{
|
||||
context->type = GSMimeEncodingBinary;
|
||||
supported = YES;
|
||||
}
|
||||
else if ([value characterAtIndex: 0] == '7')
|
||||
{
|
||||
context->type = GSMimeEncodingSevenBit;
|
||||
supported = YES;
|
||||
}
|
||||
else if ([value characterAtIndex: 0] == '8')
|
||||
{
|
||||
context->type = GSMimeEncodingEightBit;
|
||||
supported = YES;
|
||||
}
|
||||
else if ([value isEqualToString: @"chunked"] == YES)
|
||||
{
|
||||
context->type = GSMimeEncodingChunked;
|
||||
supported = YES;
|
||||
}
|
||||
if (supported == NO)
|
||||
{
|
||||
context->type = GSMimeEncodingBinary;
|
||||
NSLog(@"Unsupported/unknown content-transfer-encoding");
|
||||
return NO;
|
||||
}
|
||||
[document deleteHeaderNamed: name]; // Should be unique
|
||||
}
|
||||
else if ([name isEqualToString: @"content-type"] == YES)
|
||||
{
|
||||
NSString *type;
|
||||
|
@ -1320,7 +1491,7 @@ parseCharacterSet(NSString *token)
|
|||
}
|
||||
else
|
||||
{
|
||||
if (context->atEnd == YES)
|
||||
if ([context atEnd] == YES)
|
||||
{
|
||||
if ([d length] > 0)
|
||||
{
|
||||
|
@ -1334,7 +1505,7 @@ parseCharacterSet(NSString *token)
|
|||
intoData: data
|
||||
withContext: context];
|
||||
|
||||
if (context->atEnd == YES)
|
||||
if ([context atEnd] == YES)
|
||||
{
|
||||
/*
|
||||
* If no content type is supplied, we assume text.
|
||||
|
@ -1533,7 +1704,8 @@ parseCharacterSet(NSString *token)
|
|||
*/
|
||||
if (lineEnd == lineStart)
|
||||
{
|
||||
unsigned lengthRemaining;
|
||||
unsigned lengthRemaining;
|
||||
NSDictionary *hdr;
|
||||
|
||||
/*
|
||||
* Overwrite the header data with the body, ready to start
|
||||
|
@ -1551,7 +1723,19 @@ parseCharacterSet(NSString *token)
|
|||
lineStart = 0;
|
||||
lineEnd = 0;
|
||||
input = 0;
|
||||
inBody = YES; /* At end of headers. */
|
||||
|
||||
/*
|
||||
* At end of headers - set up context for decoding data.
|
||||
*/
|
||||
inBody = YES;
|
||||
DESTROY(context);
|
||||
hdr = [document headerNamed: @"content-transfer-encoding"];
|
||||
if (hdr == nil)
|
||||
{
|
||||
hdr = [document headerNamed: @"transfer-encoding"];
|
||||
}
|
||||
context = [self contextFor: hdr];
|
||||
RETAIN(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue