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"
29 @interface NGMimeMessageParserDelegate : NSObject
32 @implementation NGMimeMessageParserDelegate
34 static int UseFoundationStringEncodingForMimeHeader = -1;
35 static Class NGMimeMessageParserClass = NULL;
38 if (UseFoundationStringEncodingForMimeHeader == -1) {
39 UseFoundationStringEncodingForMimeHeader
40 = [[NSUserDefaults standardUserDefaults]
41 boolForKey:@"UseFoundationStringEncodingForMimeHeader"]
44 if (NGMimeMessageParserClass == NULL) {
45 NGMimeMessageParserClass = [NGMimeMessageParser class];
49 - (id)parser:(NGMimePartParser *)_p parseHeaderField:(NSString *)_field
52 NGMimeMessageParser *parser = nil;
55 if ([_p isKindOfClass:NGMimeMessageParserClass])
58 parser = [[NGMimeMessageParserClass alloc] init];
59 v = [parser valueOfHeaderField:_field data:_data];
60 [parser release]; parser = nil;
64 - (id<NGMimeBodyParser>)parser:(NGMimePartParser *)_parser
65 bodyParserForPart:(id<NGMimePart>)_part
68 NGMimeType *contentType;
70 ctype = [_part contentType];
72 contentType = ([ctype isKindOfClass:[NGMimeType class]])
74 : [NGMimeType mimeType:[ctype stringValue]];
76 if ([[contentType type] isEqualToString:@"message"] &&
77 [[contentType subType] isEqualToString:@"rfc822"]) {
78 return [[[NGMimeRfc822BodyParser alloc] init] autorelease];
84 @end /* NGMimeMessageParserDelegate */
86 @implementation NGMimeMessageParser
88 static Class NSStringClass = Nil;
94 NSAssert2([super version] == 3,
95 @"invalid superclass (%@) version %i !",
96 NSStringFromClass([self superclass]), [super version]);
97 if (NSStringClass == Nil)
98 NSStringClass = [NSString class];
102 if ((self = [super init])) {
103 [self setDelegate:[NGMimeMessageParserDelegate new]];
110 - (id<NGMimePart>)producePartWithHeader:(NGHashMap *)_header {
111 return [NGMimeMessage messageWithHeader:_header];
114 /* header field specifics */
116 - (id)valueOfHeaderField:(NSString *)_name data:(id)_data {
117 // check data for 8-bit headerfields (RFC 2047 (MIME PART III))
119 /* check whether we got passed a string ... */
120 if ([_data isKindOfClass:NSStringClass]) {
121 NSLog(@"%s: WARNING unexpected class for headerfield %@ (value %@)",
122 __PRETTY_FUNCTION__, _name, _data);
123 return [super valueOfHeaderField:_name data:_data];
125 _data = [_data decodeQuotedPrintableValueOfMIMEHeaderField:_name];
126 return [super valueOfHeaderField:_name data:_data];
129 @end /* NGMimeMessageParser */
131 @implementation NSData(MimeQPHeaderFieldDecoding)
133 - (id)decodeQuotedPrintableValueOfMIMEHeaderField:(NSString *)_name {
134 // check data for 8-bit headerfields (RFC 2047 (MIME PART III))
135 static Class NGMimeTypeClass = Nil;
137 NGMimeMessageParser_quoted_start = 1,
138 NGMimeMessageParser_quoted_charSet = 2,
139 NGMimeMessageParser_quoted_qpData = 3,
140 NGMimeMessageParser_quoted_end = 4
141 } status = NGMimeMessageParser_quoted_start;
143 const unsigned char *bytes, *firstEq;
146 if (NSStringClass == Nil) NSStringClass = [NSString class];
147 if (NGMimeTypeClass == Nil) NGMimeTypeClass = [NGMimeType class];
149 length = [self length];
151 /* check whether the string is long enough to be quoted etc */
155 /* check whether the string contains QP tokens ... */
156 bytes = [self bytes];
158 if ((firstEq = memchr(bytes, '=', length)) == NULL)
161 /* process data ... (quoting etc) */
164 unsigned int bufLen, maxBufLen;
168 unsigned char encoding;
170 buffer = calloc(length + 13, sizeof(unichar));
172 maxBufLen = length + 3;
173 buffer[maxBufLen - 1] = '\0';
180 status = NGMimeMessageParser_quoted_start;
182 /* copy data up to first '=' sign */
183 if ((cnt = (firstEq - bytes)) > 0) {
184 for (; bufLen < cnt; bufLen++)
185 buffer[bufLen] = bytes[bufLen];
188 for (; cnt < (length-1); cnt++) {
191 if (status == NGMimeMessageParser_quoted_start) {
192 if ((bytes[cnt] == '=') && (bytes[cnt + 1] == '?')) { // found begin
194 status = NGMimeMessageParser_quoted_charSet;
197 if (bytes[cnt + 1] != '=') {
198 buffer[bufLen++] = bytes[cnt];
199 buffer[bufLen++] = bytes[cnt+1];
201 if (cnt >= length - 1)
205 buffer[bufLen++] = bytes[cnt];
209 else if (status == NGMimeMessageParser_quoted_charSet) {
213 if (bytes[cnt] == '?') {
215 [NSStringClass stringWithCString:(char *)(bytes + tmp)
219 if ((length - cnt) > 2) {
220 // set encoding (eg 'q' for quoted printable)
222 encoding = bytes[cnt];
223 cnt++; // skip encoding
224 status = NGMimeMessageParser_quoted_qpData;
226 else { // unexpected end
227 NSLog(@"WARNING: unexpected end of header");
233 else if (status == NGMimeMessageParser_quoted_qpData) {
237 if ((bytes[cnt] == '?') && (bytes[cnt + 1] == '=')) {
242 tmpData = _rfc2047Decoding(encoding, (char *)bytes + tmp, cnt - tmp);
246 create a temporary string for charset conversion ...
247 Note: the headerfield is currently held in ISO Latin 1
251 if (!UseFoundationStringEncodingForMimeHeader) {
252 tmpStr = [NSStringClass stringWithData:tmpData
253 usingEncodingNamed:charset];
256 NSStringEncoding enc;
258 enc = [NGMimeTypeClass stringEncodingForCharset:charset];
259 tmpStr = [[[NSStringClass alloc] initWithData:tmpData encoding:enc]
262 tmpLen = [tmpStr length];
264 if ((tmpLen + bufLen) < maxBufLen) {
265 [tmpStr getCharacters:(buffer + bufLen)];
269 NSLog(@"ERROR[%s]: quoted data to large --> ignored %@",
270 __PRETTY_FUNCTION__, tmpStr);
275 status = NGMimeMessageParser_quoted_start;
281 buffer[bufLen] = bytes[cnt];
285 buffer[bufLen] = '\0';
291 if (buffer && foundQP) {
292 data = [[[NSString alloc] initWithCharacters:buffer length:bufLen]
295 NSLog(@"%s: got no string for buffer '%s', length '%i' !",
303 free(buffer); buffer = NULL;
310 @end /* NSData(MimeQPHeaderFieldDecoding) */
312 @implementation NGMimeRfc822BodyParser
318 NSAssert2([super version] == 2,
319 @"invalid superclass (%@) version %i !",
320 NSStringFromClass([self superclass]), [super version]);
323 - (id)parseBodyOfPart:(id<NGMimePart>)_part data:(NSData *)_data
327 id parser; // NGMimeMessageParser
329 parser = [[NGMimeMessageParser alloc] init];
330 body = [parser parsePartFromData:_data];
331 [parser release]; parser = nil;
336 @end /* NGMimeRfc822BodyParser */