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:(NGMimePartParser *)_p parseHeaderField:(NSString *)_field
51 NGMimeMessageParser *parser = nil;
54 if ([_p isKindOfClass:NGMimeMessageParserClass])
57 parser = [[NGMimeMessageParserClass alloc] init];
58 v = [parser valueOfHeaderField:_field data:_data];
59 [parser release]; parser = nil;
63 - (id<NGMimeBodyParser>)parser:(NGMimePartParser *)_parser
64 bodyParserForPart:(id<NGMimePart>)_part
67 NGMimeType *contentType;
69 ctype = [_part contentType];
71 contentType = ([ctype isKindOfClass:[NGMimeType class]])
73 : [NGMimeType mimeType:[ctype stringValue]];
75 if ([[contentType type] isEqualToString:@"message"] &&
76 [[contentType subType] isEqualToString:@"rfc822"]) {
77 return [[[NGMimeRfc822BodyParser alloc] init] autorelease];
83 @end /* NGMimeMessageParserDelegate */
85 @implementation NGMimeMessageParser
87 static Class NSStringClass = Nil;
93 NSAssert2([super version] == 3,
94 @"invalid superclass (%@) version %i !",
95 NSStringFromClass([self superclass]), [super version]);
96 if (NSStringClass == Nil)
97 NSStringClass = [NSString class];
101 if ((self = [super init])) {
102 [self setDelegate:[NGMimeMessageParserDelegate new]];
109 - (id<NGMimePart>)producePartWithHeader:(NGHashMap *)_header {
110 return [NGMimeMessage messageWithHeader:_header];
113 /* header field specifics */
115 - (id)valueOfHeaderField:(NSString *)_name data:(id)_data {
116 // check data for 8-bit headerfields (RFC 2047 (MIME PART III))
118 /* check whether we got passed a string ... */
119 if ([_data isKindOfClass:NSStringClass]) {
120 NSLog(@"%s: WARNING unexpected class for headerfield %@ (value %@)",
121 __PRETTY_FUNCTION__, _name, _data);
122 return [super valueOfHeaderField:_name data:_data];
124 _data = [_data decodeQuotedPrintableValueOfMIMEHeaderField:_name];
125 return [super valueOfHeaderField:_name data:_data];
128 @end /* NGMimeMessageParser */
130 @implementation NSData(MimeQPHeaderFieldDecoding)
132 - (id)decodeQuotedPrintableValueOfMIMEHeaderField:(NSString *)_name {
133 // check data for 8-bit headerfields (RFC 2047 (MIME PART III))
134 static Class NGMimeTypeClass = Nil;
136 NGMimeMessageParser_quoted_start = 1,
137 NGMimeMessageParser_quoted_charSet = 2,
138 NGMimeMessageParser_quoted_qpData = 3,
139 NGMimeMessageParser_quoted_end = 4
140 } status = NGMimeMessageParser_quoted_start;
142 const unsigned char *bytes, *firstEq;
145 if (NSStringClass == Nil) NSStringClass = [NSString class];
146 if (NGMimeTypeClass == Nil) NGMimeTypeClass = [NGMimeType class];
148 length = [self length];
150 /* check whether the string is long enough to be quoted etc */
154 /* check whether the string contains QP tokens ... */
155 bytes = [self bytes];
157 if ((firstEq = memchr(bytes, '=', length)) == NULL)
160 /* process data ... (quoting etc) */
163 unsigned int bufLen, maxBufLen;
167 unsigned char encoding;
169 buffer = calloc(length + 13, sizeof(unichar));
171 maxBufLen = length + 3;
172 buffer[maxBufLen - 1] = '\0';
179 status = NGMimeMessageParser_quoted_start;
181 /* copy data up to first '=' sign */
182 if ((cnt = (firstEq - bytes)) > 0) {
183 for (; bufLen < cnt; bufLen++)
184 buffer[bufLen] = bytes[bufLen];
187 for (; cnt < (length-1); cnt++) {
190 if (status == NGMimeMessageParser_quoted_start) {
191 if ((bytes[cnt] == '=') && (bytes[cnt + 1] == '?')) { // found begin
193 status = NGMimeMessageParser_quoted_charSet;
196 if (bytes[cnt + 1] != '=') {
197 buffer[bufLen++] = bytes[cnt];
198 buffer[bufLen++] = bytes[cnt+1];
200 if (cnt >= length - 1)
204 buffer[bufLen++] = bytes[cnt];
208 else if (status == NGMimeMessageParser_quoted_charSet) {
212 if (bytes[cnt] == '?') {
214 [NSStringClass stringWithCString:(char *)(bytes + tmp)
218 if ((length - cnt) > 2) {
219 // set encoding (eg 'q' for quoted printable)
221 encoding = bytes[cnt];
222 cnt++; // skip encoding
223 status = NGMimeMessageParser_quoted_qpData;
225 else { // unexpected end
226 NSLog(@"WARNING: unexpected end of header");
232 else if (status == NGMimeMessageParser_quoted_qpData) {
236 if ((bytes[cnt] == '?') && (bytes[cnt + 1] == '=')) {
241 tmpData = _rfc2047Decoding(encoding, (char *)bytes + tmp, cnt - tmp);
245 create a temporary string for charset conversion ...
246 Note: the headerfield is currently held in ISO Latin 1
250 if (!UseFoundationStringEncodingForMimeHeader) {
251 tmpStr = [NSStringClass stringWithData:tmpData
252 usingEncodingNamed:charset];
255 NSStringEncoding enc;
257 enc = [NGMimeTypeClass stringEncodingForCharset:charset];
258 tmpStr = [[[NSStringClass alloc] initWithData:tmpData encoding:enc]
261 tmpLen = [tmpStr length];
263 if ((tmpLen + bufLen) < maxBufLen) {
264 [tmpStr getCharacters:(buffer + bufLen)];
268 NSLog(@"ERROR[%s]: quoted data to large --> ignored %@",
269 __PRETTY_FUNCTION__, tmpStr);
274 status = NGMimeMessageParser_quoted_start;
280 buffer[bufLen] = bytes[cnt];
284 buffer[bufLen] = '\0';
290 if (buffer && foundQP) {
291 data = [[[NSString alloc] initWithCharacters:buffer length:bufLen]
294 NSLog(@"%s: got no string for buffer '%s', length '%i' !",
302 free(buffer); buffer = NULL;
309 @end /* NSData(MimeQPHeaderFieldDecoding) */
311 @implementation NGMimeRfc822BodyParser
317 NSAssert2([super version] == 2,
318 @"invalid superclass (%@) version %i !",
319 NSStringFromClass([self superclass]), [super version]);
322 - (id)parseBodyOfPart:(id<NGMimePart>)_part data:(NSData *)_data
326 id parser; // NGMimeMessageParser
328 parser = [[NGMimeMessageParser alloc] init];
329 body = [parser parsePartFromData:_data];
330 [parser release]; parser = nil;
335 @end /* NGMimeRfc822BodyParser */