2
Copyright (C) 2000-2005 SKYRIX Software AG
4
This file is part of SOPE.
6
SOPE is free software; you can redistribute it and/or modify it under
7
the terms of the GNU Lesser General Public License as published by the
8
Free Software Foundation; either version 2, or (at your option) any
11
SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12
WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
14
License for more details.
16
You should have received a copy of the GNU Lesser General Public
17
License along with SOPE; see the file COPYING. If not, write to the
18
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
22
#include "NGMimeMessageParser.h"
23
#include "NGMimeMessage.h"
28
@interface NGMimeMessageParserDelegate : NSObject
31
@implementation NGMimeMessageParserDelegate
33
static int UseFoundationStringEncodingForMimeHeader = -1;
34
static Class NGMimeMessageParserClass = NULL;
37
if (UseFoundationStringEncodingForMimeHeader == -1) {
38
UseFoundationStringEncodingForMimeHeader
39
= [[NSUserDefaults standardUserDefaults]
40
boolForKey:@"UseFoundationStringEncodingForMimeHeader"]
43
if (NGMimeMessageParserClass == NULL) {
44
NGMimeMessageParserClass = [NGMimeMessageParser class];
48
- (id)parser:(id)_parser parseHeaderField:(NSString *)_field data:(NSData *)_data
52
if ([_parser isKindOfClass:NGMimeMessageParserClass] == NO) {
53
NGMimeMessageParser *parser = nil;
55
parser = [[NGMimeMessageParserClass alloc] init];
56
v = [parser valueOfHeaderField:_field data:_data];
57
[parser release]; parser = nil;
62
- (id<NGMimeBodyParser>)parser:(NGMimePartParser *)_parser
63
bodyParserForPart:(id<NGMimePart>)_part
66
NGMimeType *contentType;
68
ctype = [_part contentType];
70
contentType = ([ctype isKindOfClass:[NGMimeType class]])
72
: [NGMimeType mimeType:[ctype stringValue]];
74
if ([[contentType type] isEqualToString:@"message"] &&
75
[[contentType subType] isEqualToString:@"rfc822"]) {
76
return [[[NGMimeRfc822BodyParser alloc] init] autorelease];
82
@end /* NGMimeMessageParserDelegate */
84
@implementation NGMimeMessageParser
86
static Class NSStringClass = Nil;
92
NSAssert2([super version] == 3,
93
@"invalid superclass (%@) version %i !",
94
NSStringFromClass([self superclass]), [super version]);
95
if (NSStringClass == Nil)
96
NSStringClass = [NSString class];
100
if ((self = [super init])) {
101
[self setDelegate:[NGMimeMessageParserDelegate new]];
108
- (id<NGMimePart>)producePartWithHeader:(NGHashMap *)_header {
109
return [NGMimeMessage messageWithHeader:_header];
112
/* header field specifics */
114
- (id)valueOfHeaderField:(NSString *)_name data:(id)_data {
115
// check data for 8-bit headerfields (RFC 2047 (MIME PART III))
117
/* check whether we got passed a string ... */
118
if ([_data isKindOfClass:NSStringClass]) {
119
NSLog(@"%s: WARNING unexpected class for headerfield %@ (value %@)",
120
__PRETTY_FUNCTION__, _name, _data);
121
return [super valueOfHeaderField:_name data:_data];
123
_data = [_data decodeQuotedPrintableValueOfMIMEHeaderField:_name];
124
return [super valueOfHeaderField:_name data:_data];
127
@end /* NGMimeMessageParser */
129
@implementation NSData(MimeQPHeaderFieldDecoding)
131
- (id)decodeQuotedPrintableValueOfMIMEHeaderField:(NSString *)_name {
132
// check data for 8-bit headerfields (RFC 2047 (MIME PART III))
133
static Class NGMimeTypeClass = Nil;
135
NGMimeMessageParser_quoted_start = 1,
136
NGMimeMessageParser_quoted_charSet = 2,
137
NGMimeMessageParser_quoted_qpData = 3,
138
NGMimeMessageParser_quoted_end = 4
139
} status = NGMimeMessageParser_quoted_start;
141
const unsigned char *bytes, *firstEq;
144
if (NSStringClass == Nil) NSStringClass = [NSString class];
145
if (NGMimeTypeClass == Nil) NGMimeTypeClass = [NGMimeType class];
147
length = [self length];
149
/* check whether the string is long enough to be quoted etc */
153
/* check whether the string contains QP tokens ... */
154
bytes = [self bytes];
156
if ((firstEq = memchr(bytes, '=', length)) == NULL)
159
/* process data ... (quoting etc) */
162
unsigned int bufLen, maxBufLen;
166
unsigned char encoding;
168
buffer = calloc(length + 13, sizeof(unichar));
170
maxBufLen = length + 3;
171
buffer[maxBufLen - 1] = '\0';
178
status = NGMimeMessageParser_quoted_start;
180
/* copy data up to first '=' sign */
181
if ((cnt = (firstEq - bytes)) > 0) {
182
for (; bufLen < cnt; bufLen++)
183
buffer[bufLen] = bytes[bufLen];
186
for (; cnt < (length-1); cnt++) {
189
if (status == NGMimeMessageParser_quoted_start) {
190
if ((bytes[cnt] == '=') && (bytes[cnt + 1] == '?')) { // found begin
192
status = NGMimeMessageParser_quoted_charSet;
195
if (bytes[cnt + 1] != '=') {
196
buffer[bufLen++] = bytes[cnt];
197
buffer[bufLen++] = bytes[cnt+1];
199
if (cnt >= length - 1)
203
buffer[bufLen++] = bytes[cnt];
207
else if (status == NGMimeMessageParser_quoted_charSet) {
211
if (bytes[cnt] == '?') {
213
[NSStringClass stringWithCString:(bytes + tmp) length:cnt - tmp];
216
if ((length - cnt) > 2) {
217
// set encoding (eg 'q' for quoted printable)
219
encoding = bytes[cnt];
220
cnt++; // skip encoding
221
status = NGMimeMessageParser_quoted_qpData;
223
else { // unexpected end
224
NSLog(@"WARNING: unexpected end of header");
230
else if (status == NGMimeMessageParser_quoted_qpData) {
234
if ((bytes[cnt] == '?') && (bytes[cnt + 1] == '=')) {
239
tmpData = _rfc2047Decoding(encoding, bytes + tmp, cnt - tmp);
243
create a temporary string for charset conversion ...
244
Note: the headerfield is currently held in ISO Latin 1
248
if (!UseFoundationStringEncodingForMimeHeader) {
249
tmpStr = [NSStringClass stringWithData:tmpData
250
usingEncodingNamed:charset];
253
NSStringEncoding enc;
255
enc = [NGMimeTypeClass stringEncodingForCharset:charset];
256
tmpStr = [[[NSStringClass alloc] initWithData:tmpData encoding:enc]
259
tmpLen = [tmpStr length];
261
if ((tmpLen + bufLen) < maxBufLen) {
262
[tmpStr getCharacters:(buffer + bufLen)];
266
NSLog(@"ERROR[%s]: quoted data to large --> ignored %@",
267
__PRETTY_FUNCTION__, tmpStr);
272
status = NGMimeMessageParser_quoted_start;
276
if (appendLC == YES) {
278
buffer[bufLen] = bytes[cnt];
282
buffer[bufLen] = '\0';
288
if (buffer && foundQP) {
289
data = [[[NSString alloc] initWithCharacters:buffer length:bufLen]
292
NSLog(@"%s: got no string for buffer '%s', length '%i' !",
300
free(buffer); buffer = NULL;
307
@end /* NSData(MimeQPHeaderFieldDecoding) */
309
@implementation NGMimeRfc822BodyParser
315
NSAssert2([super version] == 2,
316
@"invalid superclass (%@) version %i !",
317
NSStringFromClass([self superclass]), [super version]);
320
- (id)parseBodyOfPart:(id<NGMimePart>)_part data:(NSData *)_data
324
id parser; // NGMimeMessageParser
326
parser = [[NGMimeMessageParser alloc] init];
327
body = [parser parsePartFromData:_data];
328
[parser release]; parser = nil;
333
@end /* NGMimeRfc822BodyParser */