/* 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; 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"]) { 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 + (int)version { return 2; } + (void)initialize { NSAssert2([super version] == 2, @"invalid superclass (%@) version %i !", NSStringFromClass([self superclass]), [super version]); } /* header field specifics */ - (NSData *)generateDataForHeaderField:(NSString *)_headerField value:(id)_value { NSData *data = nil; data = [super generateDataForHeaderField:_headerField value:_value]; { const char *bytes = NULL; unsigned int length = 0; unsigned int desLen = 0; char *des = NULL; unsigned int cnt = 0; BOOL doEnc = NO; NSString *str; #if APPLE_Foundation_LIBRARY || NeXT_Foundation_LIBRARY str = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding]; #else str = [[NSString alloc] initWithData:data encoding:NSISOLatin9StringEncoding]; #endif str = [str autorelease]; bytes = [str cString]; length = [str cStringLength]; while (cnt < length) { if ((unsigned char)bytes[cnt] > 127) { doEnc = YES; break; } cnt++; } if (doEnc) { char iso[] = "=?iso-8859-15?q?"; unsigned isoLen = 16; char isoEnd[] = "?="; unsigned isoEndLen = 2; desLen = length * 3 + 20; des = calloc(desLen + 2, sizeof(char)); // memcpy(des, bytes, cnt); memcpy(des, iso, isoLen); desLen = NGEncodeQuotedPrintableMime(bytes, length, des + isoLen, desLen - isoLen); if ((int)desLen != -1) { memcpy(des + isoLen + desLen, isoEnd, isoEndLen); data = [NSData dataWithBytesNoCopy:des length:(isoLen + desLen + isoEndLen)]; } else { NSLog(@"WARNING: An error occour during quoted-printable decoding"); if (des != NULL) free(des); } } } return data; } - (BOOL)isMultiValueCommaHeaderField:(NSString *)_headerField { // currently checks for: to, cc, bcc unsigned len; unichar c0, c1; if ((len = [_headerField length]) < 2) return 0; 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 NO; } - (NSData *)generateDataWithHeaderField:(NSString *)_headerField values:(NSEnumerator *)_values { NSMutableData *res = nil; NSData *data = nil; id value = nil; const char *bytes = NULL; unsigned len = 0; if (![self isMultiValueCommaHeaderField:_headerField]) return [super generateDataWithHeaderField:_headerField values:_values]; /* generate values separated by "," */ bytes = [_headerField cString]; len = [_headerField length]; while (len > 0) { // skip leading spaces if (*bytes != ' ') break; bytes++; len--; } res = nil; while ((value = [_values nextObject]) != nil) { if (res == nil) { res = [NSMutableData dataWithCapacity:128]; [res appendBytes:bytes length:len]; [res appendBytes:": " length:2]; } else [res appendBytes:", " length:2]; data = [self generateDataForHeaderField:_headerField value:value]; [res appendData:data]; } if (res != nil) [res appendBytes:"\r\n" length:2]; return res; } /* content-transfer-encoding */ - (id)defaultBodyGenerator { NGMimeMessageBodyGenerator *gen; gen = [[NGMimeMessageBodyGenerator alloc] init]; [gen setUseMimeData:self->useMimeData]; return gen; } - (id)generatorForBodyOfPart:(id)_part { id bodyGen; NGMimeType *contentType; NSString *type; Class generatorClass; if (self->delegateRespondsTo.generatorGeneratorForBodyOfPart) { bodyGen = [self->delegate mimePartGenerator:self generatorForBodyOfPart:self->part]; if (bodyGen != nil) return bodyGen; } if ((contentType = [_part contentType]) == nil) contentType = [self defaultContentTypeForPart:_part]; if (contentType == nil) { NSLog(@"WARNING(%s): missing content-type in part 0x%08X.", _part); return nil; } type = [contentType type]; generatorClass = Nil; if ([type isEqualToString:NGMimeTypeMultipart]) generatorClass = [NGMimeMessageMultipartBodyGenerator class]; else if ([type isEqualToString:NGMimeTypeText]) generatorClass = [NGMimeMessageTextBodyGenerator class]; else if (([type isEqualToString:NGMimeTypeMessage]) && [[contentType subType] isEqualToString:@"rfc822"]) { generatorClass = [NGMimeMessageRfc822BodyGenerator class]; } bodyGen = [[[generatorClass alloc] init] autorelease]; [(id)bodyGen setUseMimeData:self->useMimeData]; return bodyGen; } @end /* NGMimeMessageGenerator */