/* header field specifics */
+- (BOOL)isMultiValueCommaHeaderField:(NSString *)_headerField {
+ /*
+ This is called by the superclass when generating fields.
+
+ Currently checks for: to, cc, bcc
+ */
+ unsigned len;
+ unichar c0, c1;
+
+ if ((len = [_headerField length]) < 2)
+ return [super isMultiValueCommaHeaderField:_headerField];
+
+ c0 = [_headerField characterAtIndex:0];
+ c1 = [_headerField characterAtIndex:1];
+
+ switch (len) {
+ case 2:
+ if ((c0 == 't' || c0 == 'T') && ((c1 == 'o' || c1 == 'O')))
+ return YES;
+ if ((c0 == 'c' || c0 == 'C') && ((c1 == 'c' || c1 == 'C')))
+ return YES;
+ break;
+ case 3:
+ if ((c0 == 'b' || c0 == 'B') && ((c1 == 'c' || c1 == 'C'))) {
+ c0 = [_headerField characterAtIndex:2];
+ if (c0 == 'c' || c0 == 'C')
+ return YES;
+ }
+ break;
+ }
+ return [super isMultiValueCommaHeaderField:_headerField];
+}
+
- (id)_escapeHeaderFieldValue:(NSData *)_data {
const char *bytes = NULL;
unsigned int length = 0;
#import <NGMime/NGPart.h>
#import <NGMime/NGMimeGeneratorProtocols.h>
-@class NSMutableData, NSData, NSString, NGHashMap, NGMutableHashMap;
+@class NSMutableData, NSData, NSString, NSEnumerator;
+@class NGHashMap, NGMutableHashMap;
@class NGMimeType, NGMimePartGenerator;
@interface NGMimePartGenerator : NSObject <NGMimePartGenerator>
/* build data with the specified header; */
-- (NSData *)generateDataWithHeaderField:(NSString *)_headerField
- values:(NSEnumerator *)_enumerator;
+- (BOOL)isMultiValueCommaHeaderField:(NSString *)_headerField;
+- (BOOL)appendHeaderField:(NSString *)_field values:(NSEnumerator *)_values
+ toData:(NSMutableData *)_data;
/* looking for a NGMimeBodyGenerator in dependece to the content-type */
- (id<NGMimeBodyGenerator>)generatorForBodyOfPart:(id<NGMimePart>)_part;
@implementation NGMimePartGenerator
static NSProcessInfo *Pi = nil;
-static BOOL debugOn = NO;
+static BOOL debugOn = NO;
+ (int)version {
return 2;
- (NSData *)generateDataForHeaderField:(NSString *)_headerField
value:(id)_value
{
- NSData *data = nil;
+ NSData *data;
- if (self->delegateRespondsTo.generatorGenerateDataForHeaderField)
+ if (self->delegateRespondsTo.generatorGenerateDataForHeaderField) {
data = [self->delegate mimePartGenerator:self
generateDataForHeaderField:_headerField
value:_value];
+ }
else {
data = [[self generatorForHeaderField:_headerField]
generateDataForHeaderFieldNamed:_headerField
return data;
}
-- (NSData *)generateDataWithHeaderField:(NSString *)_headerField
- values:(NSEnumerator *)_values
+- (BOOL)isMultiValueCommaHeaderField:(NSString *)_headerField {
+ /*
+ This is used by NGMimeMessageGenerator to encode multivalue To/Cc/Bcc
+ in a single line.
+ */
+ return NO;
+}
+
+- (BOOL)appendHeaderField:(NSString *)_field values:(NSEnumerator *)_values
+ toData:(NSMutableData *)_data
{
- NSMutableData *res = nil;
- NSData *data = nil;
- id value = nil;
- const char *bytes = NULL;
- unsigned len = 0;
+ /* returns whether data was generated */
+ const unsigned char *fcname;
+ id value = nil;
+ unsigned len;
+ BOOL isMultiValue, isFirst;
- res = [NSMutableData dataWithCapacity:64];
- bytes = [_headerField cString];
- len = [_headerField length];
- while (len > 0) {
- if (*bytes != ' ')
+ /* get field name and strip leading spaces */
+ fcname = [_field cString];
+ for (len = [_field cStringLength]; len > 0; fcname++, len--) {
+ if (*fcname != ' ')
break;
- bytes++;
- len--;
}
+
+ isMultiValue = [self isMultiValueCommaHeaderField:_field];
+ isFirst = YES;
while ((value = [_values nextObject]) != nil) {
- data = [self generateDataForHeaderField:(NSString *)_headerField
- value:value];
- [res appendBytes:bytes length:len];
- [res appendBytes:": " length:2];
- [res appendData:data];
- [res appendBytes:"\r\n" length:2];
+ NSData *data;
+
+ if ((data = [self generateDataForHeaderField:_field value:value]) == nil)
+ continue;
+
+ if (isMultiValue) {
+ if (isFirst) {
+ [_data appendBytes:fcname length:len];
+ [_data appendBytes:": " length:2];
+ isFirst = NO;
+ }
+ else
+ [_data appendBytes:", " length:2];
+
+ [_data appendData:data];
+ }
+ else {
+ [_data appendBytes:fcname length:len];
+ [_data appendBytes:": " length:2];
+ [_data appendData:data];
+ [_data appendBytes:"\r\n" length:2];
+ }
}
- return res;
+ if (!isFirst && isMultiValue) [_data appendBytes:"\r\n" length:2];
+ return isFirst;
}
-
- (NSData *)generateHeaderData:(NGHashMap *)_additionalHeaders {
NSEnumerator *headerFieldNames = nil;
NSString *headerFieldName = nil;
NGMutableHashMap *addHeaders = nil;
- NSMutableData *data = nil;
-
+ NSMutableData *data;
+
data = (self->useMimeData)
? [[[NGMimeJoinedData alloc] init] autorelease]
: [NSMutableData dataWithCapacity:2048];
headerFieldNames = [self->part headerFieldNames];
addHeaders = [_additionalHeaders mutableCopy];
-
- while ((headerFieldName = [headerFieldNames nextObject])) {
- NSData *headerFieldData = nil;
- NSEnumerator *enumerator = nil;
+
+ while ((headerFieldName = [headerFieldNames nextObject]) != nil) {
+ NSEnumerator *enumerator;
BOOL reset;
-
+
if ([[_additionalHeaders objectsForKey:headerFieldName] count] > 0) {
enumerator = [addHeaders objectEnumeratorForKey:headerFieldName];
reset = YES;
reset = NO;
enumerator = [self->part valuesOfHeaderFieldWithName:headerFieldName];
}
- headerFieldData = [self generateDataWithHeaderField:headerFieldName
- values:enumerator];
- if (reset)
- [addHeaders removeAllObjectsForKey:headerFieldName];
- if (headerFieldData)
- [data appendData:headerFieldData];
+ [self appendHeaderField:headerFieldName values:enumerator toData:data];
+
+ if (reset) [addHeaders removeAllObjectsForKey:headerFieldName];
}
+
headerFieldNames = [addHeaders keyEnumerator];
- while ((headerFieldName = [headerFieldNames nextObject])) {
- NSData *headerFieldData = nil;
- headerFieldData = [self generateDataWithHeaderField:headerFieldName
- values:[addHeaders objectEnumeratorForKey:
- headerFieldName]];
- if (headerFieldData)
- [data appendData:headerFieldData];
+ while ((headerFieldName = [headerFieldNames nextObject]) != nil) {
+ [self appendHeaderField:headerFieldName
+ values:[addHeaders objectEnumeratorForKey:headerFieldName]
+ toData:data];
}
[addHeaders release]; addHeaders = nil;
return data;
}
- (NGMimeType *)defaultContentTypeForPart:(id<NGMimePart>)_part {
- return [NGMimeType mimeType:@"application/octet-stream"];
+ static NGMimeType *octetStreamType = nil;
+ if (octetStreamType == nil)
+ octetStreamType = [[NGMimeType mimeType:@"application/octet-stream"] copy];
+ return octetStreamType;
}
- (id<NGMimeBodyGenerator>)defaultBodyGenerator {