diff --git a/Headers/gnustep/base/GSMime.h b/Headers/gnustep/base/GSMime.h index 0d6717aec..5c0a34f57 100644 --- a/Headers/gnustep/base/GSMime.h +++ b/Headers/gnustep/base/GSMime.h @@ -50,6 +50,9 @@ BOOL atEnd; /* Flag to say that data has ended. */ } - (BOOL) atEnd; +- (BOOL) decodeData: (const void*)sData + length: (unsigned)length + intoData: (NSMutableData*)dData; - (void) setAtEnd: (BOOL)flag; @end @@ -62,6 +65,8 @@ } + (NSString*) makeQuoted: (NSString*)v; + (NSString*) makeToken: (NSString*)t; +- (id) initWithName: (NSString*)n + value: (NSString*)v; - (id) initWithName: (NSString*)n value: (NSString*)v parameters: (NSDictionary*)p; @@ -116,6 +121,8 @@ - (NSMutableData*) rawMimeData; - (NSMutableData*) rawMimeData: (BOOL)isOuter; - (void) setContent: (id)newContent; +- (void) setContent: (id)newContent + type: (NSString*)type; - (void) setContent: (id)newContent type: (NSString*)type name: (NSString*)name; diff --git a/Source/Additions/GSMime.m b/Source/Additions/GSMime.m index 009b0f47b..7090c3ec5 100644 --- a/Source/Additions/GSMime.m +++ b/Source/Additions/GSMime.m @@ -327,8 +327,10 @@ wordData(NSString *word) } /** + * Coding contexts are objects used by the parser to store the state of + * decoding incoming data while it is being incrementally parsed.
* The most rudimentary context ... this is used for decoding plain - * text and binary dat (ie data which is not really decoded at all) + * text and binary data (ie data which is not really decoded at all) * and all other decoding work is done by a subclass. */ @implementation GSMimeCodingContext @@ -348,6 +350,21 @@ wordData(NSString *word) return RETAIN(self); } +/** + * Decode length bytes of data from sData and append the results to dData.
+ * Return YES on succes, NO if there is an error. + */ +- (BOOL) decodeData: (const void*)sData + length: (unsigned)length + intoData: (NSMutableData*)dData +{ + unsigned size = [dData length]; + + [dData setLength: size + length]; + memcpy([dData mutableBytes] + size, sData, length); + return YES; +} + /** * Sets the current value of the 'atEnd' flag. */ @@ -365,6 +382,96 @@ wordData(NSString *word) } @end @implementation GSMimeBase64DecoderContext +- (BOOL) decodeData: (const void*)sData + length: (unsigned)length + intoData: (NSMutableData*)dData +{ + unsigned size = [dData length]; + unsigned char *src = (unsigned char*)sData; + unsigned char *end = src + length; + unsigned char *beg; + unsigned char *dst; + + /* + * 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; + + /* + * 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 == '=') + { + [self setAtEnd: YES]; + cc = -1; + } + else if (cc == '-') + { + [self setAtEnd: YES]; + break; + } + else + { + cc = -1; /* ignore */ + } + + if (cc >= 0) + { + buf[pos++] = cc; + if (pos == 4) + { + pos = 0; + decodebase64(dst, buf); + dst += 3; + } + } + } + + /* + * Odd characters at end of decoded data need to be added separately. + */ + if ([self atEnd] == YES && pos > 0) + { + unsigned len = pos - 1;; + + while (pos < 4) + { + buf[pos++] = '\0'; + } + pos = 0; + decodebase64(dst, buf); + size += len; + } + [dData setLength: size + dst - beg]; + return YES; +} @end @interface GSMimeQuotedDecoderContext : GSMimeCodingContext @@ -375,6 +482,62 @@ wordData(NSString *word) } @end @implementation GSMimeQuotedDecoderContext +- (BOOL) decodeData: (const void*)sData + length: (unsigned)length + intoData: (NSMutableData*)dData +{ + unsigned size = [dData length]; + unsigned char *src = (unsigned char*)sData; + unsigned char *end = src + length; + unsigned char *beg; + unsigned char *dst; + + /* + * 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 (pos > 0) + { + if ((*src == '\n') || (*src == '\r')) + { + pos = 0; + } + else + { + buf[pos++] = *src; + if (pos == 3) + { + int c; + int val; + + pos = 0; + c = buf[1]; + val = isdigit(c) ? (c - '0') : (c - 55); + val *= 0x10; + c = buf[2]; + val += isdigit(c) ? (c - '0') : (c - 55); + *dst++ = val; + } + } + } + else if (*src == '=') + { + buf[pos++] = '='; + } + else + { + *dst++ = *src; + } + src++; + } + [dData setLength: size + dst - beg]; + return YES; +} @end @interface GSMimeChunkedDecoderContext : GSMimeCodingContext @@ -429,14 +592,14 @@ wordData(NSString *word) *

*

* You supply the document to be parsed as one or more data - * items passed to the Parse: method, and (if - * the method always returns YES, you give it - * a final nil argument to mark the end of the + * items passed to the -parse: method, and (if + * the method always returns YES, you give it + * a final nil argument to mark the end of the * document. *

*

* On completion of parsing a valid document, the - * document method returns the resulting parsed document. + * -document method returns the resulting parsed document. *

*/ @implementation GSMimeParser @@ -487,6 +650,9 @@ wordData(NSString *word) * 8bit (no coding actually performed) * chunked (for HTTP/1.1) * + * To add new coding schemes to the parser, you need to ovrride + * this method to return a new coding context for your scheme + * when the info argument indicates that this is appropriate. */ - (GSMimeCodingContext*) contextFor: (GSMimeHeader*)info { @@ -575,8 +741,10 @@ wordData(NSString *word) * object. *

*

- * You may override this method in order to implement - * additional coding schemes. + * You may override this method in order to implement additional + * coding schemes, but usually it should be enough for you to + * implement a custom GSMimeCodingContext subclass fotr this method + * to use. *

*/ - (BOOL) decodeData: (NSData*)sData @@ -584,13 +752,8 @@ wordData(NSString *word) intoData: (NSMutableData*)dData withContext: (GSMimeCodingContext*)con { - unsigned size = [dData length]; unsigned len = [sData length]; - unsigned char *beg; - unsigned char *dst; - const char *src; - const char *end; - Class ccls; + BOOL result = YES; if (dData == nil || [con isKindOfClass: [GSMimeCodingContext class]] == NO) { @@ -601,156 +764,29 @@ wordData(NSString *word) GS_RANGE_CHECK(aRange, len); /* - * Get pointers into source data buffer. + * Chunked decoding is relatively complex ... it makes sense to do it + * here, in order to make use of parser facilities, rather than having + * the decoding context do the work. In this case the context is used + * solely to store state information. */ - src = (const char *)[sData bytes]; - src += aRange.location; - end = src + aRange.length; - - ccls = [con class]; - if (ccls == [GSMimeBase64DecoderContext class]) - { - GSMimeBase64DecoderContext *ctxt; - - ctxt = (GSMimeBase64DecoderContext*)con; - - /* - * 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; - - /* - * 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++] = *src; - 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]) + if ([con class] == [GSMimeChunkedDecoderContext class]) { GSMimeChunkedDecoderContext *ctxt; - const char *footers = src; + unsigned size = [dData length]; + unsigned char *beg; + unsigned char *dst; + const char *src; + const char *end; + const char *footers = src; ctxt = (GSMimeChunkedDecoderContext*)con; + /* + * Get pointers into source data buffer. + */ + src = (const char *)[sData bytes]; + src += aRange.location; + end = src + aRange.length; beg = 0; /* * Make sure buffer is big enough, and set up output pointers. @@ -949,12 +985,9 @@ wordData(NSString *word) } else { - /* - * Assume binary (no) decoding required. - */ - [dData setLength: size + (end - src)]; - dst = (unsigned char*)[dData mutableBytes]; - memcpy(&dst[size], src, (end - src)); + result = [con decodeData: [sData bytes] + aRange.location + length: aRange.length + intoData: dData]; } /* @@ -965,7 +998,7 @@ wordData(NSString *word) [con setAtEnd: YES]; } - return YES; + return result; } - (NSString*) description @@ -2552,6 +2585,16 @@ static NSCharacterSet *tokenSet = nil; return [self initWithName: @"unknown" value: @"none" parameters: nil]; } +/** + * Convenience method calling -initWithName:value:parameters: with the + * supplied argument and nil parameters. + */ +- (id) initWithName: (NSString*)n + value: (NSString*)v +{ + return [self initWithName: n value: v parameters: nil]; +} + /** * * Initialise a GSMimeHeader supplying a name, a value and a dictionary @@ -2585,6 +2628,9 @@ static NSCharacterSet *tokenSet = nil; return [objects objectForKey: k]; } +/** + * Returns a dictionary of all the additional objects for the header. + */ - (NSDictionary*) objects { return AUTORELEASE([objects copy]); @@ -2607,7 +2653,7 @@ static NSCharacterSet *tokenSet = nil; /** * Returns the parameters of this header ... a dictionary whose keys - * are all lowercase strings, and whosre value is a string which may + * are all lowercase strings, and whose values are strings which may * contain mixed case. */ - (NSDictionary*) parameters @@ -2723,7 +2769,7 @@ static NSCharacterSet *tokenSet = nil; /** * Method to store specific information for particular types of - * header. + * header. This is used for non-standard parts of headers. */ - (void) setObject: (id)o forKey: (NSString*)k { @@ -3858,6 +3904,18 @@ static NSCharacterSet *tokenSet = nil; } } +/** + * Convenience method calling -setContent:type:name: to set document + * content and type without specifying a name ... useful for top-level + * documents rather than parts within a daocument (parts should really + * be named). + */ +- (void) setContent: (id)newContent + type: (NSString*)type +{ + [self setContent: newContent type: type name: nil]; +} + /** * Convenience method calling -setContent:type:subType:name: to set * content and type. If the type argument contains a slash '/') diff --git a/Source/NSDistributedLock.m b/Source/NSDistributedLock.m index 3a72ad1cd..64118a678 100644 --- a/Source/NSDistributedLock.m +++ b/Source/NSDistributedLock.m @@ -46,6 +46,10 @@ static NSFileManager *mgr = nil; } } +/** + * Return a distributed lock for aPath. + * See -initWithPath: for details. + */ + (NSDistributedLock*) lockWithPath: (NSString*)aPath { return AUTORELEASE([[self alloc] initWithPath: aPath]); @@ -67,7 +71,7 @@ static NSFileManager *mgr = nil; if ([mgr removeFileAtPath: _lockPath handler: nil] == NO) { - NSString *err = GSLastErrorStr(errno); + const char *err = GSLastErrorStr(errno); attributes = [mgr fileAttributesAtPath: _lockPath traverseLink: YES]; if ([modDate isEqual: [attributes fileModificationDate]] == YES) @@ -90,7 +94,10 @@ static NSFileManager *mgr = nil; /** * Initialises the reciever with the specified filesystem path.
* The location in the filesystem must be accessible for this - * to be usable. + * to be usable. That is, the processes using the lock must be able + * to access, create, and destroy files at the path.
+ * The directory in which the last path component resides must already + * exist ... create it using NSFileManager if you need to. */ - (NSDistributedLock*) initWithPath: (NSString*)aPath {