From a7a3e1833676b37fc4350f0b20491bde69588cb5 Mon Sep 17 00:00:00 2001 From: helge Date: Sun, 30 Jan 2005 19:33:33 +0000 Subject: [PATCH] major change to MIME generation, a lot of cleanups git-svn-id: http://svn.opengroupware.org/SOPE/trunk@534 e4a50df8-12e2-0310-a44c-efbce7f8a7e3 --- sope-mime/ChangeLog | 12 + sope-mime/NGImap4/ChangeLog | 4 + sope-mime/NGImap4/NGImap4Client.m | 22 +- sope-mime/NGImap4/NGImap4Folder.m | 2 +- sope-mime/NGMail/ChangeLog | 6 + sope-mime/NGMail/GNUmakefile | 2 + sope-mime/NGMail/NGMailBase64Encoding.m | 97 +++++ sope-mime/NGMail/NGMimeMessage.m | 39 +- sope-mime/NGMail/NGMimeMessageBodyGenerator.m | 2 + sope-mime/NGMail/NGMimeMessageGenerator.m | 100 ++--- sope-mime/NGMail/NGMimeMessageParser.m | 2 +- sope-mime/NGMime/ChangeLog | 18 + sope-mime/NGMime/GNUmakefile | 2 + sope-mime/NGMime/NGMimeBodyGenerator.m | 372 +++--------------- .../NGMimeContentTypeHeaderFieldGenerator.m | 4 +- sope-mime/NGMime/NGMimeJoinedData.h | 8 + sope-mime/NGMime/NGMimeJoinedData.m | 56 ++- .../NGMime/NGMimeMultipartBodyGenerator.m | 358 +++++++++++++++++ sope-mime/NGMime/NGMimePartGenerator.m | 183 ++++++--- sope-mime/NGMime/NGPart.m | 2 +- sope-mime/Version | 2 +- 21 files changed, 784 insertions(+), 509 deletions(-) create mode 100644 sope-mime/NGMail/NGMailBase64Encoding.m create mode 100644 sope-mime/NGMime/NGMimeMultipartBodyGenerator.m diff --git a/sope-mime/ChangeLog b/sope-mime/ChangeLog index 058be256..c047e104 100644 --- a/sope-mime/ChangeLog +++ b/sope-mime/ChangeLog @@ -1,3 +1,15 @@ +2005-01-30 Helge Hess + + * v4.5.206 + + * NGMail: fixed a crasher in a warn-log (occurred if no content-type + was set), major reorgs + + * NGMime: some code cleanups, added MIME generation debugging, + improved NGMimeJoinedData, make reorgs + + * NGImap4: minor code cleanups + 2005-01-04 Helge Hess * NGImap4: fixed a small warning with Xcode (v4.5.205) diff --git a/sope-mime/NGImap4/ChangeLog b/sope-mime/NGImap4/ChangeLog index 11cf5b79..66b1316d 100644 --- a/sope-mime/NGImap4/ChangeLog +++ b/sope-mime/NGImap4/ChangeLog @@ -1,3 +1,7 @@ +2005-01-30 Helge Hess + + * NGImap4Client.m: minor code cleanups + 2005-01-04 Helge Hess * NGSieveClient.m: fixed a warning when compiling with Xcode diff --git a/sope-mime/NGImap4/NGImap4Client.m b/sope-mime/NGImap4/NGImap4Client.m index 429cc073..cf3059b5 100644 --- a/sope-mime/NGImap4/NGImap4Client.m +++ b/sope-mime/NGImap4/NGImap4Client.m @@ -873,24 +873,25 @@ static BOOL ImapDebugEnabled = NO; while (cntOld < (len - 1)) { if (old[cntOld] == '\n') { - new[cntNew++] = '\r'; - new[cntNew++] = '\n'; + new[cntNew] = '\r'; cntNew++; + new[cntNew] = '\n'; cntNew++; } else if (old[cntOld] != '\r') { - new[cntNew++] = old[cntOld]; + new[cntNew] = old[cntOld]; cntNew++; } cntOld++; } if (old[cntOld] == '\n') { - new[cntNew++] = '\r'; - new[cntNew++] = '\n'; + new[cntNew] = '\r'; cntNew++; + new[cntNew] = '\n'; cntNew++; } else if (old[cntOld] != '\r') { - new[cntNew++] = old[cntOld]; - } - message = [(NSString *)[NSString alloc] - initWithCString:new length:cntNew]; - if (new) free(new); new = NULL; + new[cntNew] = old[cntOld]; cntNew++; + } + + // TODO: fix this junk, do not treat the message as a string, its NSData + message = [(NSString *)[NSString alloc] initWithCString:new length:cntNew]; + if (new != NULL) free(new); new = NULL; } icmd = [NSString stringWithFormat:@"append \"%@\" (%@) {%d}", @@ -900,6 +901,7 @@ static BOOL ImapDebugEnabled = NO; result = [self processCommand:icmd withTag:YES withNotification:NO]; + // TODO: explain that if ([[result objectForKey:@"ContinuationResponse"] boolValue]) result = [self processCommand:message withTag:NO]; diff --git a/sope-mime/NGImap4/NGImap4Folder.m b/sope-mime/NGImap4/NGImap4Folder.m index 4cffb67e..4c6af2c1 100644 --- a/sope-mime/NGImap4/NGImap4Folder.m +++ b/sope-mime/NGImap4/NGImap4Folder.m @@ -1200,7 +1200,7 @@ static int FetchNewUnseenMessagesInSubFoldersOnDemand = -1; return YES; } [self resetLastException]; - + dict = [[self->context client] select:[self absoluteName]]; if (![self _checkResult:dict cmd:__PRETTY_FUNCTION__]) { self->failedFlags.select = YES; diff --git a/sope-mime/NGMail/ChangeLog b/sope-mime/NGMail/ChangeLog index ae6b0005..009fae08 100644 --- a/sope-mime/NGMail/ChangeLog +++ b/sope-mime/NGMail/ChangeLog @@ -1,3 +1,9 @@ +2005-01-30 Helge Hess + + * NGMimeMessageGenerator.m: fixed a format bug in an error log which + could lead to a crash, moved base64 encoding function to separate + file + 2004-12-14 Marcus Mueller * NGMail.xcode: minor fixes and updated diff --git a/sope-mime/NGMail/GNUmakefile b/sope-mime/NGMail/GNUmakefile index 5f250f5f..1d1cd041 100644 --- a/sope-mime/NGMail/GNUmakefile +++ b/sope-mime/NGMail/GNUmakefile @@ -45,6 +45,8 @@ NGMail_OBJC_FILES = \ NGMimeMessageMultipartBodyGenerator.m \ NGMimeMessageRfc822BodyGenerator.m \ NGMimeMessageTextBodyGenerator.m \ + \ + NGMailBase64Encoding.m \ -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/subproject.make diff --git a/sope-mime/NGMail/NGMailBase64Encoding.m b/sope-mime/NGMail/NGMailBase64Encoding.m new file mode 100644 index 00000000..f4554c1b --- /dev/null +++ b/sope-mime/NGMail/NGMailBase64Encoding.m @@ -0,0 +1,97 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of SOPE. + + SOPE is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + SOPE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with SOPE; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#include "NGMimeMessageGenerator.h" +#include "NGMimeMessage.h" +#include +#include "common.h" + +/* Defaults + Mail_Use_8bit_Encoding_For_Text[BOOL] -- + Use 8bit content-transfer-encoding for + text messages +*/ + +NSData * +_base64Encoding(NGMimeBodyGenerator *self, + NSData *_data_, + id_part, + NGMutableHashMap *_addHeaders) +{ + NSString *transEnc = nil; + const char *bytes = NULL; + unsigned length = 0; + + /* kinda hack, treat NGMimeFileData objects as already encoded */ + + if ([_data_ isKindOfClass:[NGMimeFileData class]]) + return _data_; + + /* encoding */ + + bytes = [_data_ bytes]; + length = [_data_ length]; + + while (length > 0) { + if ((unsigned char)*bytes > 127) { + break; + } + bytes++; + length--; + } + if (length > 0) { // should be encoded + NGMimeType *type; + + type = [_part contentType]; + + if ([[type type] isEqualToString:@"text"]) { + NSUserDefaults *ud; + BOOL use8bit; + + ud = [NSUserDefaults standardUserDefaults]; + use8bit = [ud boolForKey:@"Mail_Use_8bit_Encoding_For_Text"]; + + if (use8bit) + transEnc = @"8bit"; + else { + _data_ = [_data_ dataByEncodingQuotedPrintable]; + transEnc = @"quoted-printable"; + } + } + else { + NGMimeType *appOctet; + + _data_ = [_data_ dataByEncodingBase64]; + transEnc = @"base64"; + + appOctet = [NGMimeType mimeType:@"application" subType:@"octet-stream"]; + if (type == nil) + [_addHeaders setObject:appOctet forKey:@"content-type"]; + } + } + else /* no encoding */ + transEnc = @"7bit"; + + [_addHeaders setObject:transEnc forKey:@"content-transfer-encoding"]; + [_addHeaders setObject:[NSNumber numberWithInt:[_data_ length]] + forKey:@"content-length"]; + return _data_; +} diff --git a/sope-mime/NGMail/NGMimeMessage.m b/sope-mime/NGMail/NGMimeMessage.m index d058d15c..38b85102 100644 --- a/sope-mime/NGMail/NGMimeMessage.m +++ b/sope-mime/NGMail/NGMimeMessage.m @@ -67,9 +67,9 @@ static NGMimeType *defaultDataType = nil; /* NGPart */ - (NSEnumerator *)valuesOfHeaderFieldWithName:(NSString *)_name { - if ([_name isEqualToString:@"content-type"] == YES) { + if ([_name isEqualToString:@"content-type"]) return [[NSArray arrayWithObject:[self contentType]] objectEnumerator]; - } + return [self->header objectEnumeratorForKey:_name]; } - (NSEnumerator *)headerFieldNames { @@ -87,29 +87,22 @@ static NGMimeType *defaultDataType = nil; /* NGMimePart */ - (NGMimeType *)autodetectContentType { - NGMimeType *type = nil; + const char *bytes; + unsigned length; - if ((self->body != nil) && - ([self->body isKindOfClass:[NSData class]] == YES)) { - const char *bytes = NULL; - unsigned length = 0; - - bytes = [self->body bytes]; - length = [self->body length]; - - while (length > 0) { - if ((unsigned char)*bytes > 127) { - break; - } - bytes++; - length--; - } - type = (length > 0) ? defaultDataType : defaultTextType; - } - else - type = defaultTextType; + if (!((self->body != nil) && [self->body isKindOfClass:[NSData class]])) + return defaultTextType; - return type; + bytes = [self->body bytes]; + length = [self->body length]; + while (length > 0) { + if ((unsigned char)*bytes > 127) + break; + + bytes++; + length--; + } + return (length > 0) ? defaultDataType : defaultTextType; } - (NGMimeType *)contentType { diff --git a/sope-mime/NGMail/NGMimeMessageBodyGenerator.m b/sope-mime/NGMail/NGMimeMessageBodyGenerator.m index f897d0ef..0f176bb7 100644 --- a/sope-mime/NGMail/NGMimeMessageBodyGenerator.m +++ b/sope-mime/NGMail/NGMimeMessageBodyGenerator.m @@ -34,6 +34,8 @@ NSStringFromClass([self superclass]), [super version]); } +/* encoding data */ + - (NSData *)encodeData:(NSData *)_data forPart:(id)_part additionalHeaders:(NGMutableHashMap *)_addHeaders diff --git a/sope-mime/NGMail/NGMimeMessageGenerator.m b/sope-mime/NGMail/NGMimeMessageGenerator.m index 6026bd4e..415b34c3 100644 --- a/sope-mime/NGMail/NGMimeMessageGenerator.m +++ b/sope-mime/NGMail/NGMimeMessageGenerator.m @@ -24,83 +24,23 @@ #include #include "common.h" -/* Defaults - Mail_Use_8bit_Encoding_For_Text[BOOL] -- - Use 8bit content-transfer-encoding for - text messages -*/ - -NSData * -_base64Encoding(NGMimeBodyGenerator *self, - NSData *_data_, - id_part, - NGMutableHashMap *_addHeaders) -{ - NSString *transEnc = nil; - const char *bytes = NULL; - unsigned length = 0; - - if ([_data_ isKindOfClass:[NGMimeFileData class]]) - return _data_; - - bytes = [_data_ bytes]; - length = [_data_ length]; - - while (length > 0) { - if ((unsigned char)*bytes > 127) { - break; - } - bytes++; - length--; - } - if (length > 0) { // should be encoded - NGMimeType *type; - - type = [_part contentType]; - - if ([[type type] isEqualToString:@"text"] == YES) { - NSUserDefaults *ud; - BOOL use8bit; - - ud = [NSUserDefaults standardUserDefaults]; - use8bit = [ud boolForKey:@"Mail_Use_8bit_Encoding_For_Text"]; - - if (use8bit) - transEnc = @"8bit"; - else { - _data_ = [_data_ dataByEncodingQuotedPrintable]; - transEnc = @"quoted-printable"; - } - } - else { - NGMimeType *appOctet; - - _data_ = [_data_ dataByEncodingBase64]; - transEnc = @"base64"; - - appOctet = [NGMimeType mimeType:@"application" subType:@"octet-stream"]; - if (type == nil) - [_addHeaders setObject:appOctet forKey:@"content-type"]; - } - } - else /* no encoding */ - transEnc = @"7bit"; - - [_addHeaders setObject:transEnc forKey:@"content-transfer-encoding"]; - [_addHeaders setObject:[NSNumber numberWithInt:[_data_ length]] - forKey:@"content-length"]; - return _data_; -} - @implementation NGMimeMessageGenerator +static BOOL debugOn = NO; + + (int)version { return 2; } + (void)initialize { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSAssert2([super version] == 2, @"invalid superclass (%@) version %i !", NSStringFromClass([self superclass]), [super version]); + + debugOn = [ud boolForKey:@"NGMimeGeneratorDebugEnabled"]; + if (debugOn) + NSLog(@"WARNING[%@]: NGMimeGeneratorDebugEnabled is enabled!", self); } /* header field specifics */ @@ -181,6 +121,7 @@ _base64Encoding(NGMimeBodyGenerator *self, } - (id)generatorForBodyOfPart:(id)_part { + /* called by -generateBodyData:? */ id bodyGen; NGMimeType *contentType; NSString *type; @@ -198,7 +139,8 @@ _base64Encoding(NGMimeBodyGenerator *self, contentType = [self defaultContentTypeForPart:_part]; if (contentType == nil) { - NSLog(@"WARNING(%s): missing content-type in part 0x%08X.", _part); + [self logWithFormat:@"WARNING(%s): missing content-type in part 0x%08X.", + __PRETTY_FUNCTION__, _part]; return nil; } @@ -213,10 +155,30 @@ _base64Encoding(NGMimeBodyGenerator *self, [[contentType subType] isEqualToString:@"rfc822"]) { generatorClass = [NGMimeMessageRfc822BodyGenerator class]; } + + if (generatorClass == Nil) { + [self debugWithFormat: + @"found no body generator class for part with type: %@", + contentType]; + return nil; + } + + if (debugOn) { + [self debugWithFormat:@"using body generator class %@ for part: %@", + generatorClass, _part]; + } + + /* allocate generator */ bodyGen = [[[generatorClass alloc] init] autorelease]; [(id)bodyGen setUseMimeData:self->useMimeData]; return bodyGen; } +/* debugging */ + +- (BOOL)isDebuggingEnabled { + return debugOn; +} + @end /* NGMimeMessageGenerator */ diff --git a/sope-mime/NGMail/NGMimeMessageParser.m b/sope-mime/NGMail/NGMimeMessageParser.m index 692538d1..c373c151 100644 --- a/sope-mime/NGMail/NGMimeMessageParser.m +++ b/sope-mime/NGMail/NGMimeMessageParser.m @@ -273,7 +273,7 @@ static Class NSStringClass = Nil; } } } - if (appendLC == YES) { + if (appendLC) { if (cnt < length) { buffer[bufLen] = bytes[cnt]; bufLen++; diff --git a/sope-mime/NGMime/ChangeLog b/sope-mime/NGMime/ChangeLog index 351c7ee8..fbba16db 100644 --- a/sope-mime/NGMime/ChangeLog +++ b/sope-mime/NGMime/ChangeLog @@ -1,3 +1,21 @@ +2005-01-30 Helge Hess + + * NGMimeContentTypeHeaderFieldGenerator.m, NGPart.m: fixed default + type ("application/octet-stream", not "application/octet") + + * NGMimePartGenerator.m: code cleanups, added support for + 'NGMimeGeneratorDebugEnabled' default, properly generate \r\n instead + of just \n as a header/body separator, add a hack to avoid duplicate + generation of the \r\n header/body separator (needs to get tested) + + * NGMimeJoinedData.m: code cleanups, added a -length method + + * NGMimeMultipartBodyGenerator.m: enable debug logs when + 'NGMimeGeneratorDebugEnabled' default is enabled + + * NGMimeBodyGenerator.m: moved NGMimeMultipartBodyGenerator to own + file, added support for 'NGMimeGeneratorDebugEnabled' default + 2004-12-14 Marcus Mueller * NGMime.xcode: minor fixes and updated diff --git a/sope-mime/NGMime/GNUmakefile b/sope-mime/NGMime/GNUmakefile index 7165de9e..77a84140 100644 --- a/sope-mime/NGMime/GNUmakefile +++ b/sope-mime/NGMime/GNUmakefile @@ -69,6 +69,8 @@ NGMime_OBJC_FILES = \ NGMimeHeaderFieldGeneratorSet.m \ NGMimeRFC822DateHeaderFieldGenerator.m \ NGMimeStringHeaderFieldGenerator.m \ + \ + NGMimeMultipartBodyGenerator.m \ -include GNUmakefile.preamble include $(GNUSTEP_MAKEFILES)/subproject.make diff --git a/sope-mime/NGMime/NGMimeBodyGenerator.m b/sope-mime/NGMime/NGMimeBodyGenerator.m index 28e755a0..9c687f11 100644 --- a/sope-mime/NGMime/NGMimeBodyGenerator.m +++ b/sope-mime/NGMime/NGMimeBodyGenerator.m @@ -19,29 +19,48 @@ 02111-1307, USA. */ -#import "NGMimeBodyGenerator.h" -#import "NGMimePartGenerator.h" -#import "NGMimeMultipartBody.h" -#import "NGMimeJoinedData.h" -#import "NGMimeFileData.h" -#import "common.h" -#include +#include "NGMimeBodyGenerator.h" +#include "NGMimePartGenerator.h" +#include "common.h" @implementation NGMimeBodyGenerator +static BOOL debugOn = NO; + + (int)version { return 2; } ++ (void)initialize { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + + debugOn = [ud boolForKey:@"NGMimeGeneratorDebugEnabled"]; + if (debugOn) + NSLog(@"WARNING[%@]: NGMimeGeneratorDebugEnabled is enabled!", self); +} + +/* generate data for body */ - (NSData *)generateBodyOfPart:(id)_part additionalHeaders:(NGMutableHashMap *)_addHeaders delegate:(id)_delegate { - return [self encodeData:[_part body] - forPart:_part - additionalHeaders:_addHeaders]; + NSData *data, *input; + + input = [_part body]; + data = [self encodeData:input + forPart:_part + additionalHeaders:_addHeaders]; + if (debugOn) { + [self debugWithFormat:@"encoded %d bytes to %d bytes (same=%s, class=%@)", + [input length], [data length], + input == data ? "yes" : "no", + NSStringFromClass([data class])]; + } + return data; } +/* properly encode data for transfer (eg to 7bit for email) */ + - (NSData *)encodeData:(NSData *)_data forPart:(id)_part additionalHeaders:(NGMutableHashMap *)_addHeaders @@ -49,16 +68,24 @@ return _data; } +/* manage data storage */ + +- (void)setUseMimeData:(BOOL)_b { + self->useMimeData = _b; +} - (BOOL)useMimeData { return self->useMimeData; } -- (void)setUseMimeData:(BOOL)_b { - self->useMimeData = _b; +/* debugging */ + +- (BOOL)isDebuggingEnabled { + return debugOn; } @end /* NGMimeBodyGenerator */ + @implementation NGMimeTextBodyGenerator + (int)version { @@ -74,13 +101,15 @@ additionalHeaders:(NGMutableHashMap *)_addHeaders delegate:(id)_delegate { - NSStringEncoding encoding = [NSString defaultCStringEncoding]; - NSData *data = nil; - id body = nil; + NSStringEncoding encoding; + NSData *data; + id body; + encoding = [NSString defaultCStringEncoding]; body = [_part body]; - + if ([body isKindOfClass:[NSString class]]) { + // TODO: deal with charset in content-type! data = [body dataUsingEncoding:encoding]; } else @@ -100,6 +129,7 @@ @end /* NGMimeTextBodyGenerator */ + @implementation NGMimeRfc822BodyGenerator + (int)version { @@ -113,9 +143,8 @@ - (id)generatorForPart:(id)_part { id g; - - g = [[[NGMimePartGenerator allocWithZone:[self zone]] init] - autorelease]; + + g = [[[NGMimePartGenerator alloc] init] autorelease]; [g setUseMimeData:self->useMimeData]; return g; } @@ -124,8 +153,8 @@ additionalHeaders:(NGMutableHashMap *)_addHeaders delegate:(id)_delegate { - NSData *data = nil; - NGMimePartGenerator *gen = nil; + NSData *data; + NGMimePartGenerator *gen; gen = (NGMimePartGenerator *)[self generatorForPart:_part]; [gen setDelegate:_delegate]; @@ -134,304 +163,3 @@ } @end /* NGMimeRfc822BodyGenerator */ - - -@implementation NGMimeMultipartBodyGenerator - -static Class NGMimeFileDataClass = Nil; -static Class NGMimeJoinedDataClass = Nil; - -+ (int)version { - return 2; -} -+ (void)initialize { - NSAssert2([super version] == 2, - @"invalid superclass (%@) version %i !", - NSStringFromClass([self superclass]), [super version]); - - NGMimeFileDataClass = [NGMimeFileData class]; - NGMimeJoinedDataClass = [NGMimeJoinedData class]; -} - -+ (NSString *)boundaryPrefix { - static NSString *BoundaryPrefix = nil; - - if (BoundaryPrefix == nil) { - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; - BoundaryPrefix = - [[ud stringForKey:@"NGMime_MultipartBoundaryPrefix"] copy]; - if (BoundaryPrefix == nil) - BoundaryPrefix = @"--=_=-_OpenGroupware_org_NGMime"; - } - return BoundaryPrefix; -} - -static inline BOOL _isBoundaryInArray(NGMimeMultipartBodyGenerator *self, - NSString *_boundary, - NSArray *_data) -{ - const unsigned char *boundary; - unsigned int length; - NSEnumerator *enumerator; - NSData *data; - BOOL wasFound; - - boundary = [_boundary cString]; - length = [_boundary length]; - enumerator = [_data objectEnumerator]; - data = nil; - wasFound = NO; - - while ((data = [enumerator nextObject])) { - const unsigned char *bytes; - unsigned int dataLen; - unsigned cnt; - - if ([data isKindOfClass:NGMimeFileDataClass] || - [data isKindOfClass:NGMimeJoinedDataClass]) - continue; - - bytes = [data bytes]; - dataLen = [data length]; - cnt = 0; - - if (dataLen < length) - return NO; - - while ((cnt < dataLen) && ((dataLen - cnt) >= length)) { - if (bytes[cnt + 2] != '-') { // can`t be a boundary - cnt++; - continue; - } - - if (bytes[cnt] == '\n') {// LF*- - if (bytes[cnt + 1] == '-') { // LF-- - if (strncmp(boundary, bytes + cnt + 3, length) == 0) { - wasFound = YES; - break; - } - } - } - else if (bytes[cnt] == '\r') { //CR*- - if (bytes[cnt + 1] == '-') { //CR-- - if (strncmp(boundary, bytes + cnt + 3, length) == 0) { - wasFound = YES; - break; - } - } - else if ((bytes[cnt + 1] == '\n') && (bytes[cnt + 3] == '-')) { - if (strncmp(boundary, bytes + cnt + 4, length) == 0) { // CRLF-- - wasFound = YES; - break; - } - } - } - cnt++; - } - } - return wasFound; -} - -- (NSString *)buildBoundaryForPart:(id)_part data:(NSArray *)_data - additionalHeaders:(NGMutableHashMap *)_addHeaders -{ - static int BoundaryUniqueCount = 0; - NSString *boundary = nil; - BOOL isUnique = NO; - unsigned pid; - - if ((boundary = [[_part contentType] valueOfParameter:@"boundary"])) - return boundary; - -#if defined(__WIN32__) - pid = GetCurrentProcessId(); -#else - pid = getpid(); -#endif - - boundary = [NSString stringWithFormat: - @"--%@-%d-%f-%d------", - [NGMimeMultipartBodyGenerator boundaryPrefix], - pid, [[NSDate date] timeIntervalSince1970], - BoundaryUniqueCount++]; - while (!isUnique) { - isUnique = _isBoundaryInArray(self, boundary, _data) ? NO : YES; - if (!isUnique) - boundary = [NSString stringWithFormat: - @"--%@-%d-%f-%d-----", - [NGMimeMultipartBodyGenerator boundaryPrefix], - pid, [[NSDate date] timeIntervalSince1970], - BoundaryUniqueCount++]; - } - { // setting content-type with boundary - NGMimeType *type = nil; - - type = [_part contentType]; - - if (type == nil) { - NSDictionary *d; - - d = [[NSDictionary alloc] initWithObjectsAndKeys: - boundary, @"boundary", nil]; - type = [NGMimeType mimeType:@"multipart" subType:@"mixed" - parameters:d]; - [d release]; - } - else { - NSMutableDictionary *dict = nil; - - dict = [NSMutableDictionary dictionaryWithDictionary: - [type parametersAsDictionary]]; - [dict setObject:boundary forKey:@"boundary"]; - type = [NGMimeType mimeType:[type type] subType:[type subType] - parameters:dict]; - } - [_addHeaders setObject:type forKey:@"content-type"]; - } - return boundary; -} - -- (NSData *)buildDataWithBoundary:(NSString *)_boundary - partsData:(NSArray *)_parts -{ - NSEnumerator *enumerator; - NSData *part; - NSMutableData *data; - - data = (self->useMimeData) - ? [[[NGMimeJoinedData alloc] init] autorelease] - : [NSMutableData dataWithCapacity:4096]; - - enumerator = [_parts objectEnumerator]; - while ((part = [enumerator nextObject])) { - [data appendBytes:"--" length:2]; - [data appendBytes:[_boundary cString] length:[_boundary length]]; - [data appendBytes:"\n" length:1]; - [data appendData:part]; - [data appendBytes:"\n" length:1]; - } - [data appendBytes:"--" length:2]; - [data appendBytes:[_boundary cString] length:[_boundary length]]; - [data appendBytes:"--\n" length:3]; - return data; -} - -- (NSData *)generateBodyOfPart:(id)_part - additionalHeaders:(NGMutableHashMap *)_addHeaders - delegate:(id)_delegate -{ - // TODO: split up - NGMimeMultipartBody *body = nil; - NSMutableData *data = nil; - id tmp = nil; - NSArray *parts = nil; - id part = nil; - NSEnumerator *enumerator = nil; - NSString *boundary = nil; - NSMutableArray *partsData = nil; - NSAutoreleasePool *pool; - - body = [_part body]; - - if (body == nil) - return [NSData data]; - - pool = [[NSAutoreleasePool alloc] init]; - - NSAssert1([body isKindOfClass:[NGMimeMultipartBody class]], - @"NGMimeMultipartBodyGenerator expect a NGMimeMultipartBody " - @"as body of part\n part: %@\n", _part); - - data = (self->useMimeData) - ? [[[NGMimeJoinedData alloc] init] autorelease] - : [NSMutableData dataWithCapacity:4096]; - - if ([_delegate respondsToSelector: - @selector(multipartBodyGenerator:prefixForPart:)]) - tmp = [_delegate multipartBodyGenerator:self prefixForPart:_part]; - else - tmp = [self multipartBodyGenerator:self prefixForPart:_part - mimeMultipart:body]; - if (tmp != nil) { - NSAssert([tmp isKindOfClass:[NSString class]], - @"prefix should be a NSString"); - [data appendBytes:[tmp cString] length:[tmp length]]; - } - - parts = [body parts]; - enumerator = [parts objectEnumerator]; - partsData = [[NSMutableArray allocWithZone:[self zone]] initWithCapacity:4]; - - while ((part = [enumerator nextObject])) { - id gen = nil; - - if ([_delegate respondsToSelector: - @selector(multipartBodyGenerator:generatorForPart:)]) { - gen = [_delegate multipartBodyGenerator:self generatorForPart:part]; - } - else { - gen = [self multipartBodyGenerator:self generatorForPart:part]; - [gen setDelegate:_delegate]; - [(id)gen setUseMimeData:self->useMimeData]; - } - if (gen == nil) { - NSLog(@"WARNING(%s): got no generator", __PRETTY_FUNCTION__); - continue; - } - tmp = [gen generateMimeFromPart:part]; - if (tmp != nil) { - [partsData addObject:tmp]; - } - } - boundary = [self buildBoundaryForPart:_part data:partsData - additionalHeaders:_addHeaders]; - tmp = [self buildDataWithBoundary:boundary partsData:partsData]; - - if (tmp != nil) { - [data appendData:tmp]; - } - else { - NSLog(@"WARNING(%s): couldn`t build multipart data", __PRETTY_FUNCTION__); - } - if ([_delegate respondsToSelector: - @selector(multipartBodyGenerator:suffixForPart:)]) - tmp = [_delegate multipartBodyGenerator:self suffixForPart:_part]; - else - tmp = [self multipartBodyGenerator:self suffixForPart:_part - mimeMultipart:body]; - if (tmp != nil) { - NSAssert([tmp isKindOfClass:[NSString class]], - @"suffix should be a NSString"); - [data appendBytes:[tmp cString] length:[tmp length]]; - } - [partsData release]; partsData = nil; - [data retain]; - [pool release]; - return [data autorelease]; -} - -- (NSString *)multipartBodyGenerator:(NGMimeMultipartBodyGenerator *)_gen - prefixForPart:(id)_part - mimeMultipart:(NGMimeMultipartBody *)_body { - - return @""; // [_body prefix]; -} - -- (NSString *)multipartBodyGenerator:(NGMimeMultipartBodyGenerator *)_gen - suffixForPart:(id)_part - mimeMultipart:(NGMimeMultipartBody *)_body { - - return @""; //[_body suffix]; -} - -- (id)multipartBodyGenerator:(NGMimeBodyGenerator *)_gen - generatorForPart:(id)_part -{ - id gen; - - gen = [[NGMimePartGenerator alloc] init]; - [gen setUseMimeData:self->useMimeData]; - return [gen autorelease]; -} - -@end /* NGMimeMultipartBodyGenerator */ diff --git a/sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m b/sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m index 395fe64d..5fba916f 100644 --- a/sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m +++ b/sope-mime/NGMime/NGMimeContentTypeHeaderFieldGenerator.m @@ -42,7 +42,7 @@ if (type == nil) { NSLog(@"WARNING(%s): empty content type field", __PRETTY_FUNCTION__); - return [NSData dataWithBytes:"application/octet" length:17]; + return [NSData dataWithBytes:"application/octet-stream" length:24]; } if ([_value isKindOfClass:[NSString class]]) { return [_value dataUsingEncoding:NSUTF8StringEncoding]; @@ -51,7 +51,7 @@ if (![type isKindOfClass:[NGMimeType class]]) { NSLog(@"WARNING(%s): invalid MIME type value (%@) !", __PRETTY_FUNCTION__, type); - return [NSData dataWithBytes:"application/octet" length:17]; + return [NSData dataWithBytes:"application/octet" length:24]; } data = [NSMutableData dataWithCapacity:64]; diff --git a/sope-mime/NGMime/NGMimeJoinedData.h b/sope-mime/NGMime/NGMimeJoinedData.h index 192fc780..1af6a8be 100644 --- a/sope-mime/NGMime/NGMimeJoinedData.h +++ b/sope-mime/NGMime/NGMimeJoinedData.h @@ -24,6 +24,14 @@ #import +/* + NGMimeJoinedData + + TODO: explain. + + Note: -copyWithZone: returns a _retained_ copy. Bug or feature? +*/ + @class NSMutableArray; @interface NGMimeJoinedData : NSObject diff --git a/sope-mime/NGMime/NGMimeJoinedData.m b/sope-mime/NGMime/NGMimeJoinedData.m index 9b4375ea..83c450c7 100644 --- a/sope-mime/NGMime/NGMimeJoinedData.m +++ b/sope-mime/NGMime/NGMimeJoinedData.m @@ -42,14 +42,29 @@ [super dealloc]; } -- (id)copyWithZone:(NSZone*)zone { - return RETAIN(self); +/* NSCopying */ + +- (id)copyWithZone:(NSZone *)_zone { + // TODO: we are mutable, is -retain a bug or feature? + return [self retain]; } +/* data */ + - (NSArray *)_joinedDataObjects { return self->joinedDataObjects; } +- (unsigned int)length { + unsigned int i, count, size; + + for (i = 0, count = [self->joinedDataObjects count], size = 0; i < count;i++) + size += [[self->joinedDataObjects objectAtIndex:i] length]; + return size; +} + +/* appending data */ + - (void)appendData:(NSData *)_data { if ([_data isKindOfClass:[NGMimeJoinedData class]]) { [self->joinedDataObjects addObjectsFromArray: @@ -59,25 +74,31 @@ [self->joinedDataObjects addObject:_data]; } -- (void)appendBytes:(const void *)_bytes - length:(unsigned int)_length -{ +- (void)appendBytes:(const void *)_bytes length:(unsigned int)_length { NSMutableData *data; + if (_length == 0) + return; + data = (NSMutableData *)[self->joinedDataObjects lastObject]; - + if ([data isKindOfClass:[NSMutableData class]]) { [data appendBytes:_bytes length:_length]; } else { - data = [NSMutableData dataWithBytes:_bytes length:_length]; - [self->joinedDataObjects addObject:data]; + /* + Note: we create a mutable because it is not unlikely that additional + appends are coming. + */ + data = [[NSMutableData alloc] initWithBytes:_bytes length:_length]; + if (data != nil) [self->joinedDataObjects addObject:data]; + [data release]; } } -- (BOOL)writeToFile:(NSString*)_path - atomically:(BOOL)_useAuxiliaryFile -{ +/* writing to file */ + +- (BOOL)writeToFile:(NSString*)_path atomically:(BOOL)_useAuxiliaryFile { NSString *filename = nil; NSEnumerator *enumerator; NSData *data; @@ -133,10 +154,17 @@ return result; } +/* description */ + - (NSString *)description { - return [NSString stringWithFormat:@"<0x%08X[%@]: joinedDataObjects=%d>", - self, NSStringFromClass([self class]), - [self->joinedDataObjects count]]; + NSMutableString *ms; + + ms = [NSMutableString stringWithCapacity:128]; + [ms appendFormat:@"<0x%08X[%@]:", self, NSStringFromClass([self class])]; + [ms appendFormat:@" joinedDataObjects=%d>", + [self->joinedDataObjects count]]; + [ms appendString:@">"]; + return ms; } @end /* NGMimeJoinedData */ diff --git a/sope-mime/NGMime/NGMimeMultipartBodyGenerator.m b/sope-mime/NGMime/NGMimeMultipartBodyGenerator.m new file mode 100644 index 00000000..4c0a78f1 --- /dev/null +++ b/sope-mime/NGMime/NGMimeMultipartBodyGenerator.m @@ -0,0 +1,358 @@ +/* + Copyright (C) 2000-2005 SKYRIX Software AG + + This file is part of SOPE. + + SOPE is free software; you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + SOPE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with SOPE; see the file COPYING. If not, write to the + Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. +*/ + +#include "NGMimeBodyGenerator.h" +#include "NGMimePartGenerator.h" +#include "NGMimeMultipartBody.h" +#include "NGMimeJoinedData.h" +#include "NGMimeFileData.h" +#include "common.h" +#include + +@implementation NGMimeMultipartBodyGenerator + +static Class NGMimeFileDataClass = Nil; +static Class NGMimeJoinedDataClass = Nil; +static BOOL debugOn = NO; + ++ (int)version { + return 2; +} ++ (void)initialize { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + + NSAssert2([super version] == 2, + @"invalid superclass (%@) version %i !", + NSStringFromClass([self superclass]), [super version]); + + NGMimeFileDataClass = [NGMimeFileData class]; + NGMimeJoinedDataClass = [NGMimeJoinedData class]; + + debugOn = [ud boolForKey:@"NGMimeGeneratorDebugEnabled"]; + if (debugOn) + NSLog(@"WARNING[%@]: NGMimeGeneratorDebugEnabled is enabled!", self); +} + ++ (NSString *)boundaryPrefix { + static NSString *BoundaryPrefix = nil; + + if (BoundaryPrefix == nil) { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + BoundaryPrefix = + [[ud stringForKey:@"NGMime_MultipartBoundaryPrefix"] copy]; + if (BoundaryPrefix == nil) + BoundaryPrefix = @"--=_=-_OpenGroupware_org_NGMime"; + } + return BoundaryPrefix; +} + +static inline BOOL _isBoundaryInArray(NGMimeMultipartBodyGenerator *self, + NSString *_boundary, + NSArray *_data) +{ + const unsigned char *boundary; + unsigned int length; + NSEnumerator *enumerator; + NSData *data; + BOOL wasFound; + + // TODO: do we need to treat the boundary as a CString? + boundary = [_boundary cString]; + length = [_boundary length]; + enumerator = [_data objectEnumerator]; + data = nil; + wasFound = NO; + + while ((data = [enumerator nextObject]) != nil) { + const unsigned char *bytes; + unsigned int dataLen; + unsigned cnt; + + if ([data isKindOfClass:NGMimeFileDataClass] || + [data isKindOfClass:NGMimeJoinedDataClass]) + continue; + + bytes = [data bytes]; + dataLen = [data length]; + cnt = 0; + + if (dataLen < length) + return NO; + + while ((cnt < dataLen) && ((dataLen - cnt) >= length)) { + if (bytes[cnt + 2] != '-') { // can`t be a boundary + cnt++; + continue; + } + + if (bytes[cnt] == '\n') {// LF*- + if (bytes[cnt + 1] == '-') { // LF-- + if (strncmp(boundary, bytes + cnt + 3, length) == 0) { + wasFound = YES; + break; + } + } + } + else if (bytes[cnt] == '\r') { //CR*- + if (bytes[cnt + 1] == '-') { //CR-- + if (strncmp(boundary, bytes + cnt + 3, length) == 0) { + wasFound = YES; + break; + } + } + else if ((bytes[cnt + 1] == '\n') && (bytes[cnt + 3] == '-')) { + if (strncmp(boundary, bytes + cnt + 4, length) == 0) { // CRLF-- + wasFound = YES; + break; + } + } + } + cnt++; + } + } + return wasFound; +} + +- (NSString *)buildBoundaryForPart:(id)_part data:(NSArray *)_data + additionalHeaders:(NGMutableHashMap *)_addHeaders +{ + static int BoundaryUniqueCount = 0; + NSString *boundary = nil; + BOOL isUnique = NO; + unsigned pid; + + if ((boundary = [[_part contentType] valueOfParameter:@"boundary"])) + return boundary; + +#if defined(__WIN32__) + pid = GetCurrentProcessId(); +#else + pid = getpid(); +#endif + + boundary = [NSString stringWithFormat: + @"--%@-%d-%f-%d------", + [NGMimeMultipartBodyGenerator boundaryPrefix], + pid, [[NSDate date] timeIntervalSince1970], + BoundaryUniqueCount++]; + while (!isUnique) { + isUnique = _isBoundaryInArray(self, boundary, _data) ? NO : YES; + if (!isUnique) { + boundary = [NSString stringWithFormat: + @"--%@-%d-%f-%d-----", + [NGMimeMultipartBodyGenerator boundaryPrefix], + pid, [[NSDate date] timeIntervalSince1970], + BoundaryUniqueCount++]; + } + } + { // setting content-type with boundary + NGMimeType *type = nil; + + type = [_part contentType]; + + if (type == nil) { + NSDictionary *d; + + d = [[NSDictionary alloc] initWithObjectsAndKeys: + boundary, @"boundary", nil]; + type = [NGMimeType mimeType:@"multipart" subType:@"mixed" + parameters:d]; + [d release]; + } + else { + NSMutableDictionary *dict = nil; + + dict = [NSMutableDictionary dictionaryWithDictionary: + [type parametersAsDictionary]]; + [dict setObject:boundary forKey:@"boundary"]; + type = [NGMimeType mimeType:[type type] subType:[type subType] + parameters:dict]; + } + [_addHeaders setObject:type forKey:@"content-type"]; + } + return boundary; +} + +- (NSData *)buildDataWithBoundary:(NSString *)_boundary + partsData:(NSArray *)_parts +{ + NSEnumerator *enumerator; + NSData *part; + NSMutableData *data; + + data = (self->useMimeData) + ? [[[NGMimeJoinedData alloc] init] autorelease] + : [NSMutableData dataWithCapacity:4096]; + + enumerator = [_parts objectEnumerator]; + while ((part = [enumerator nextObject])) { + [data appendBytes:"--" length:2]; + [data appendBytes:[_boundary cString] length:[_boundary length]]; + [data appendBytes:"\n" length:1]; + [data appendData:part]; + [data appendBytes:"\n" length:1]; + } + [data appendBytes:"--" length:2]; + [data appendBytes:[_boundary cString] length:[_boundary length]]; + [data appendBytes:"--\n" length:3]; + return data; +} + +- (NSData *)generateBodyOfPart:(id)_part + additionalHeaders:(NGMutableHashMap *)_addHeaders + delegate:(id)_delegate +{ + // TODO: split up + NGMimeMultipartBody *body = nil; + NSMutableData *data = nil; + id tmp = nil; + NSArray *parts = nil; + id part = nil; + NSEnumerator *enumerator = nil; + NSString *boundary = nil; + NSMutableArray *partsData = nil; + NSAutoreleasePool *pool; + + body = [_part body]; + + if (body == nil) + return [NSData data]; + + pool = [[NSAutoreleasePool alloc] init]; + + NSAssert1([body isKindOfClass:[NGMimeMultipartBody class]], + @"NGMimeMultipartBodyGenerator expect a NGMimeMultipartBody " + @"as body of part\n part: %@\n", _part); + + data = (self->useMimeData) + ? [[[NGMimeJoinedData alloc] init] autorelease] + : [NSMutableData dataWithCapacity:4096]; + + if ([_delegate respondsToSelector: + @selector(multipartBodyGenerator:prefixForPart:)]) + tmp = [_delegate multipartBodyGenerator:self prefixForPart:_part]; + else + tmp = [self multipartBodyGenerator:self prefixForPart:_part + mimeMultipart:body]; + if (tmp != nil) { + NSAssert([tmp isKindOfClass:[NSString class]], + @"prefix should be a NSString"); + [data appendBytes:[tmp cString] length:[tmp length]]; + } + + parts = [body parts]; + enumerator = [parts objectEnumerator]; + partsData = [[NSMutableArray alloc] initWithCapacity:4]; + + while ((part = [enumerator nextObject]) != nil) { + id gen = nil; + NSData *data; + + if ([_delegate respondsToSelector: + @selector(multipartBodyGenerator:generatorForPart:)]) { + gen = [_delegate multipartBodyGenerator:self generatorForPart:part]; + } + else { + gen = [self multipartBodyGenerator:self generatorForPart:part]; + [gen setDelegate:_delegate]; + [(id)gen setUseMimeData:self->useMimeData]; + } + if (gen == nil) { + [self logWithFormat:@"WARNING(%s): got no generator", + __PRETTY_FUNCTION__]; + continue; + } + + /* generate part */ + + data = [gen generateMimeFromPart:part]; + if (data != nil) { + if (debugOn) { + [self debugWithFormat: + @"multipart body generated %d bytes using %@ for part: %@", + [data length], gen, part]; + } + [partsData addObject:data]; + } + else if (debugOn) { + [self debugWithFormat: + @"multipart body %@ did not generate content for part: %@", + gen, part]; + } + } + boundary = [self buildBoundaryForPart:_part data:partsData + additionalHeaders:_addHeaders]; + tmp = [self buildDataWithBoundary:boundary partsData:partsData]; + + if (tmp != nil) { + [data appendData:tmp]; + } + else { + NSLog(@"WARNING(%s): couldn`t build multipart data", __PRETTY_FUNCTION__); + } + if ([_delegate respondsToSelector: + @selector(multipartBodyGenerator:suffixForPart:)]) + tmp = [_delegate multipartBodyGenerator:self suffixForPart:_part]; + else + tmp = [self multipartBodyGenerator:self suffixForPart:_part + mimeMultipart:body]; + if (tmp != nil) { + NSAssert([tmp isKindOfClass:[NSString class]], + @"suffix should be a NSString"); + [data appendBytes:[tmp cString] length:[tmp length]]; + } + [partsData release]; partsData = nil; + [data retain]; + [pool release]; + return [data autorelease]; +} + +- (NSString *)multipartBodyGenerator:(NGMimeMultipartBodyGenerator *)_gen + prefixForPart:(id)_part + mimeMultipart:(NGMimeMultipartBody *)_body +{ + return @""; // [_body prefix]; +} + +- (NSString *)multipartBodyGenerator:(NGMimeMultipartBodyGenerator *)_gen + suffixForPart:(id)_part + mimeMultipart:(NGMimeMultipartBody *)_body +{ + return @""; //[_body suffix]; +} + +- (id)multipartBodyGenerator:(NGMimeBodyGenerator *)_gen + generatorForPart:(id)_part +{ + id gen; + + gen = [[NGMimePartGenerator alloc] init]; + [gen setUseMimeData:self->useMimeData]; + return [gen autorelease]; +} + +/* debugging */ + +- (BOOL)isDebuggingEnabled { + return debugOn; +} + +@end /* NGMimeMultipartBodyGenerator */ diff --git a/sope-mime/NGMime/NGMimePartGenerator.m b/sope-mime/NGMime/NGMimePartGenerator.m index 6be526f2..d3793f6c 100644 --- a/sope-mime/NGMime/NGMimePartGenerator.m +++ b/sope-mime/NGMime/NGMimePartGenerator.m @@ -28,9 +28,22 @@ @implementation NGMimePartGenerator +static NSProcessInfo *Pi = nil; +static BOOL debugOn = NO; + + (int)version { return 2; } ++ (void)initialize { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + + debugOn = [ud boolForKey:@"NGMimeGeneratorDebugEnabled"]; + if (debugOn) + NSLog(@"WARNING[%@]: NGMimeGeneratorDebugEnabled is enabled!", self); + + if (Pi == nil) + Pi = [[NSProcessInfo processInfo] retain]; +} + (id)mimePartGenerator { return [[[self alloc] init] autorelease]; @@ -67,7 +80,7 @@ self->delegateRespondsTo.generatorGenerateDataForBodyOfPart = [self->delegate respondsToSelector: - @selector(mimePartGenerator:generateDataForBodyOfPart:additionalHeaders:)]; + @selector(mimePartGenerator:generateDataForBodyOfPart:additionalHeaders:)]; } - (id)delegate { @@ -75,14 +88,8 @@ } - (BOOL)prepareForGenerationOfPart:(id)_part { - { - id tmp = self->part; - - self->part = _part; - - [self->part retain]; - [tmp release]; tmp = nil; - } + ASSIGN(self->part, _part); + if (self->result) { [self->result release]; self->result = nil; @@ -90,12 +97,12 @@ self->result = (self->useMimeData) ? [[NGMimeJoinedData alloc] init] : [[NSMutableData alloc] initWithCapacity:4096]; - - if ([self->result respondsToSelector:@selector(methodForSelector:)]) + if ([self->result respondsToSelector:@selector(methodForSelector:)]) { self->appendBytes = (void(*)(id,SEL,const void *, unsigned)) [self->result methodForSelector: @selector(appendBytes:length:)]; + } else self->appendBytes = NULL; return YES; @@ -207,7 +214,7 @@ } - (NGMimeType *)defaultContentTypeForPart:(id)_part { - return [NGMimeType mimeType:@"application/octet"]; + return [NGMimeType mimeType:@"application/octet-stream"]; } - (id)defaultBodyGenerator { @@ -223,21 +230,22 @@ NGMimeType *contentType = nil; NSString *type = nil; - if (self->delegateRespondsTo.generatorGeneratorForBodyOfPart) + if (self->delegateRespondsTo.generatorGeneratorForBodyOfPart) { bodyGen = [self->delegate mimePartGenerator:self generatorForBodyOfPart:self->part]; - - if (!bodyGen) { + } + + if (bodyGen == nil) { contentType = [_part contentType]; - if (contentType == nil) { + if (contentType == nil) contentType = [self defaultContentTypeForPart:_part]; - } + if (contentType == nil) { - NSLog(@"WARNING(%s): no content-type", __PRETTY_FUNCTION__); + [self logWithFormat:@"WARNING(%s): no content-type",__PRETTY_FUNCTION__]; return nil; } type = [contentType type]; - + if ([type isEqualToString:NGMimeTypeMultipart]) { bodyGen = [[[NGMimeMultipartBodyGenerator alloc] init] autorelease]; } @@ -256,77 +264,120 @@ - (NSData *)generateBodyData:(NGMutableHashMap *)_additionalHeaders { NSData *data = nil; id bodyGen = nil; - + id body; + + /* ask delegate whether it wants to deal with the part */ + if (self->delegateRespondsTo.generatorGenerateDataForBodyOfPart) { data = [self->delegate mimePartGenerator:self generateDataForBodyOfPart:self->part additionalHeaders:_additionalHeaders]; + return data; } - else { - bodyGen = [self generatorForBodyOfPart:self->part]; - if (bodyGen == nil) // no generator for body - bodyGen = [self defaultBodyGenerator]; + /* lookup generator object or use default generator */ - [(id)bodyGen setUseMimeData:self->useMimeData]; - - if (bodyGen == nil) { - id body = [self->part body]; - NSLog(@"WARNING(%s): no defaultBodyGenerator", __PRETTY_FUNCTION__); - if ([body isKindOfClass:[NSData class]]) { - data = body; - } - else if ([body isKindOfClass:[NSString class]]) { - data = [body dataUsingEncoding:[NSString defaultCStringEncoding]]; - } - } - else { - data = [bodyGen generateBodyOfPart:self->part - additionalHeaders:_additionalHeaders - delegate:self->delegate]; + bodyGen = [self generatorForBodyOfPart:self->part]; + if (bodyGen == nil) { /* no generator for body */ + bodyGen = [self defaultBodyGenerator]; + if (debugOn) { + [self debugWithFormat:@"Note: using default generator %@ for part: %@", + bodyGen, self->part]; } } + + [(id)bodyGen setUseMimeData:self->useMimeData]; + + /* generate */ + + if (bodyGen != nil) { + data = [bodyGen generateBodyOfPart:self->part + additionalHeaders:_additionalHeaders + delegate:self->delegate]; + return data; + } + + /* fallback, try to encode NSString and NSData objects */ + + body = [self->part body]; + [self logWithFormat:@"WARNING(%s): class has no defaultBodyGenerator", + __PRETTY_FUNCTION__]; + + if ([body isKindOfClass:[NSData class]]) + data = body; + else if ([body isKindOfClass:[NSString class]]) + data = [body dataUsingEncoding:[NSString defaultCStringEncoding]]; + else + data = nil; + return data; } - (void)generateData { - NGMutableHashMap *additionalHeaders = nil; - NSData *bodyData = nil; - NSData *headerData = nil; + NGMutableHashMap *additionalHeaders; + NSData *bodyData; + NSData *headerData; + /* the body generator will fill in headers if required */ additionalHeaders = [[NGMutableHashMap alloc] initWithCapacity:16]; + + if (debugOn) { + [self debugWithFormat:@"generate part: 0x%08X<%@>", + self->part, NSStringFromClass([self->part class])]; + } + + bodyData = [self generateBodyData:additionalHeaders]; + if (debugOn) { + [self debugWithFormat:@" => body 0x%08X<%@> length=%d", + bodyData, NSStringFromClass([bodyData class]), [bodyData length]]; + } - bodyData = [self generateBodyData:additionalHeaders]; headerData = [self generateHeaderData:additionalHeaders]; - + if (debugOn) { + [self debugWithFormat:@" => header 0x%08X<%@> length=%d", + headerData, NSStringFromClass([headerData class]), + [headerData length]]; + } + if (headerData != nil) { - [self->result appendData:headerData]; - [self->result appendBytes:"\n" length:1]; + // TODO: length check is new, this should be correct, but might be wrong. + // Some strange things occur if we generate a part which is a + // NGMimeFileData (this method was called twiced, once without header + // data which in turn resulted in a superflous "\r\n" string + if ([headerData length] > 0) { + [self->result appendData:headerData]; + [self->result appendBytes:"\r\n" length:2]; + } + if (bodyData != nil) [self->result appendData:bodyData]; + else if (debugOn) + [self debugWithFormat:@" => did not generate any body data!"]; } - [additionalHeaders release]; + else if (debugOn) + [self debugWithFormat:@" => did not generate any header data!"]; + + [additionalHeaders release]; additionalHeaders = nil; } - (NSData *)generateMimeFromPart:(id)_part { + NSData *data; + [self prepareForGenerationOfPart:_part]; - if ([self generatePrefix]) { - NSData *data; + if (![self generatePrefix]) + return nil; - [self generateData]; - [self generateSuffix]; - data = self->result; - self->result = nil; - return [data autorelease]; - } - return nil; + [self generateData]; + [self generateSuffix]; + data = self->result; + self->result = nil; + return [data autorelease]; } - (NSString *)generateMimeFromPartToFile:(id)_part { NSString *filename = nil; static NSString *TmpPath = nil; - static NSProcessInfo *Pi = nil; if (TmpPath == nil) { NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; @@ -336,11 +387,9 @@ TmpPath = @"/tmp/"; TmpPath = [[TmpPath stringByAppendingPathComponent:@"OGo"] copy]; } - if (Pi == nil) - Pi = [[NSProcessInfo processInfo] retain]; - + filename = [Pi temporaryFileName:TmpPath]; - + [self setUseMimeData:YES]; if (![[self generateMimeFromPart:_part] @@ -356,13 +405,17 @@ return self->part; } +- (void)setUseMimeData:(BOOL)_b { + self->useMimeData = _b; +} - (BOOL)useMimeData { return self->useMimeData; } -- (void)setUseMimeData:(BOOL)_b { - self->useMimeData = _b; +/* debugging */ + +- (BOOL)isDebuggingEnabled { + return debugOn; } @end /* NGMimePartGenerator */ - diff --git a/sope-mime/NGMime/NGPart.m b/sope-mime/NGMime/NGPart.m index 80010450..344d9e0f 100644 --- a/sope-mime/NGMime/NGPart.m +++ b/sope-mime/NGMime/NGPart.m @@ -68,7 +68,7 @@ - (NGMimeType *)contentType { static NGMimeType *defType = nil; if (defType == nil) - defType = [[NGMimeType mimeType:@"application/octet"] retain]; + defType = [[NGMimeType mimeType:@"application/octet-stream"] retain]; return defType; } - (NSString *)contentId { diff --git a/sope-mime/Version b/sope-mime/Version index 7c699373..74d1c71b 100644 --- a/sope-mime/Version +++ b/sope-mime/Version @@ -2,6 +2,6 @@ MAJOR_VERSION:=4 MINOR_VERSION:=5 -SUBMINOR_VERSION:=205 +SUBMINOR_VERSION:=206 # v4.2.149 requires libNGStreams v4.2.34 -- 2.39.5