]> err.no Git - sope/blob - sope-mime/NGMime/NGMimePartParser.m
fixed OGo #936
[sope] / sope-mime / NGMime / NGMimePartParser.m
1 /*
2   Copyright (C) 2000-2004 SKYRIX Software AG
3
4   This file is part of OpenGroupware.org.
5
6   OGo 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
9   later version.
10
11   OGo 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.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with OGo; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "NGMimePartParser.h"
23 #include "NGMimeBodyParser.h"
24 #include "NGMimeType.h"
25 #include "NGMimeUtilities.h"
26 #include "common.h"
27
28 /* this tunes, how big reused data cache objects may get (10MB) */
29 #define MAX_DATA_OBJECT_SIZE_CACHE (10*1024*1024)
30
31 @implementation NGMimePartParser
32
33 static Class StringClass  = Nil;
34 static Class MStringClass = Nil;
35 static Class DataClass    = Nil;
36 static Class NSMutableDataClass = NULL;
37
38 static NGMimeHeaderNames *HeaderNames = NULL;
39
40 + (int)version {
41   return 3;
42 }
43
44 static int MimeLogEnabled = -1;
45
46 + (void)initialize {
47   static BOOL isInitialized = NO;
48   NSUserDefaults *ud = [NSUserDefaults standardUserDefaults];
49   if (isInitialized) return;
50   isInitialized = YES;
51   
52   MimeLogEnabled     = [ud boolForKey:@"MimeLogEnabled"] ? 1 : 0;
53   MStringClass       = [NSMutableString class];
54   StringClass        = [NSString class];
55   DataClass          = [NSData class];
56   NSMutableDataClass = [NSMutableData class];
57 }
58
59 static inline int  _la(NGMimePartParser *self, int _la);
60 static inline void _consume(NGMimePartParser *self, int _cnt);
61 static inline BOOL _checkKey(NGMimePartParser *self, NGHashMap *_map,
62                              NSString *_key);
63
64 + (NGMimeHeaderNames *)headerFieldNames {
65   if (HeaderNames == NULL) {
66     HeaderNames = malloc(sizeof(NGMimeHeaderNames));
67
68     HeaderNames->accept                  = @"accept";
69     HeaderNames->acceptLanguage          = @"accept-language";
70     HeaderNames->acceptEncoding          = @"accept-encoding";
71     HeaderNames->acceptCharset           = @"accept-charset";
72     HeaderNames->cacheControl            = @"cache-control";
73     HeaderNames->cc                      = @"cc";
74     HeaderNames->connection              = @"connection";
75     HeaderNames->contentDisposition      = @"content-disposition";
76     HeaderNames->contentLength           = @"content-length";
77     HeaderNames->contentTransferEncoding = @"content-transfer-encoding";
78     HeaderNames->contentType             = @"content-type";
79     HeaderNames->cookie                  = @"cookie";
80     HeaderNames->date                    = @"date";
81     HeaderNames->from                    = @"from";
82     HeaderNames->host                    = @"host";
83     HeaderNames->keepAlive               = @"keep-alive";
84     HeaderNames->messageID               = @"message-id";
85     HeaderNames->mimeVersion             = @"mime-version";
86     HeaderNames->organization            = @"organization";
87     HeaderNames->received                = @"received";
88     HeaderNames->returnPath              = @"return-path";
89     HeaderNames->referer                 = @"referer";
90     HeaderNames->replyTo                 = @"reply-to";
91     HeaderNames->subject                 = @"subject";
92     HeaderNames->to                      = @"to";
93     HeaderNames->userAgent               = @"user-agent";
94     HeaderNames->xMailer                 = @"x-mailer";
95   }
96   return HeaderNames;
97 }
98
99 - (id)init {
100   if ((self = [super init])) {
101     self->bufLen        = 1024;
102     self->contentLength = -1;
103   }
104   return self;
105 }
106
107 - (void)dealloc {
108   [self->contentTransferEncoding release];
109   [self->source     release];
110   [self->sourceData release];
111   [super dealloc];
112 }
113
114 /* accessors */
115
116 - (void)setDelegate:(id)_delegate {
117   self->delegate = _delegate;
118
119   self->delegateRespondsTo.parserWillParseHeader =
120     [self->delegate respondsToSelector:@selector(parserWillParseHeader:)];
121   
122   self->delegateRespondsTo.parserDidParseHeader =
123     [self->delegate respondsToSelector:@selector(parser:didParseHeader:)];
124
125   self->delegateRespondsTo.parserKeepHeaderFieldData =
126     [self->delegate respondsToSelector:@selector(parser:keepHeaderField:data:)];
127   
128   self->delegateRespondsTo.parserKeepHeaderFieldValue =
129     [self->delegate respondsToSelector:
130                       @selector(parser:keepHeaderField:value:)];
131
132   self->delegateRespondsTo.parserFoundCommentInHeaderField =
133     [self->delegate respondsToSelector:
134                       @selector(parser:foundComment:inHeaderField:)];
135
136   self->delegateRespondsTo.parserWillParseBodyOfPart =
137     [self->delegate respondsToSelector:@selector(parser:willParseBodyOfPart:)];
138   
139   self->delegateRespondsTo.parserDidParseBodyOfPart =
140     [self->delegate respondsToSelector:@selector(parser:didParseBodyOfPart:)];
141
142   self->delegateRespondsTo.parserParseRawBodyDataOfPart =
143     [self->delegate respondsToSelector:
144                       @selector(parser:parseRawBodyData:ofPart:)];
145   
146   self->delegateRespondsTo.parserBodyParserForPart =
147     [self->delegate respondsToSelector:@selector(parser:bodyParserForPart:)];
148
149   self->delegateRespondsTo.parserDecodeBodyOfPart =
150     [self->delegate respondsToSelector:@selector(parser:decodeBody:ofPart:)];
151
152   self->delegateRespondsTo.parserParseHeaderFieldData =
153     [self->delegate respondsToSelector:@selector(parser:parseHeaderField:data:)];
154 }
155
156 - (id)delegate {
157   return self->delegate;
158 }
159
160 /* header */
161
162 - (id<NGMimeHeaderFieldParser>)parserForHeaderField:(NSString *)_name {
163   static id defParserSet = nil;
164   if (defParserSet == nil) {
165     defParserSet =
166       [[NGMimeHeaderFieldParserSet defaultRfc822HeaderFieldParserSet] retain];
167   }
168   return defParserSet;
169 }
170
171 + (NSStringEncoding)defaultHeaderFieldEncoding {
172   return NSISOLatin1StringEncoding;
173 }
174
175 - (id)valueOfHeaderField:(NSString *)_name data:(id)_data {
176   // TODO: use iconv (if available, eg not on OSX !!!) to convert
177   //       an unknown encoding to UTF-16 and create an NSConcrete*UTF16String
178   id<NGMimeHeaderFieldParser> parser;
179   NSString                    *tmp;
180   id value = nil;
181   
182   if (self->delegateRespondsTo.parserParseHeaderFieldData)
183     value = [delegate parser:self parseHeaderField:_name data:_data];
184   
185   if (value)
186     return value;
187   
188   if ([_data isKindOfClass:DataClass]) {
189     tmp = [[StringClass alloc]
190             initWithData:_data
191             encoding:[NGMimePartParser defaultHeaderFieldEncoding]];
192   }
193   else
194     tmp = [_data retain];
195   
196   if ((parser = [self parserForHeaderField:_name]))
197     value = [parser parseValue:tmp ofHeaderField:_name];
198   else
199     value = [tmp stringByTrimmingSpaces];
200   
201   value = [[value retain] autorelease];
202   [tmp release];
203   return value;
204 }
205
206 /*
207   possible constants:
208   
209   NGMime_CC                      = @"cc"; 
210   NGMime_To                      = @"to";                        : 2
211
212   NGMime_Date                    = @"date";
213   NGMime_Host                    = @"host";
214   NGMime_From                    = @"from";                      : 4
215
216   NGMime_Cookie                  = @"cookie"
217   NGMime_Accept                  = @"accept"                     : 6
218   
219   NGMime_Referer                 = @"referer"
220   NGMime_Subject                 = @"subject";                   : 7
221
222   NGMime_xMailer                 = @"x-mailer";
223   NGMime_ReplyTo                 = @"reply-to"
224   NGMime_Received                = @"received";                  : 8
225
226   NGMime_Connection              = @"connection"
227   NGMime_KeepAlive               = @"keep-alive"
228   NGMime_UserAgent               = @"user-agent";
229   NGMime_MessageID               = @"message-id";                : 10
230   
231   NGMime_ReturnPath              = @"return-path";               : 11
232
233   NGMime_MimeVersion             = @"mime-version";
234   NGMime_Organization            = @"organization";
235   NGMime_ContentType             = @"content-type";              : 12
236
237   NGMime_CacheControl            = @"cache-control"              : 13
238   
239   NGMime_AcceptCharset           = @"accept-charset"
240   NGMime_ContentLength           = @"content-length";            : 14
241
242   NGMime_AcceptEncoding          = @"accept-encoding"
243   NGMime_AcceptLanguage          = @"accept-language"            : 15
244
245   NGMime_ContentDisposition      = @"content-disposition";       : 19
246
247   NGMime_ContentTransferEncoding = @"content-transfer-encoding"; : 25
248 */
249
250 static NSString *fieldNameForCString(id self, char *_cstring, int _len) {
251   if (HeaderNames == NULL)
252     [NGMimePartParser headerFieldNames];
253   
254   switch  (_len) {
255     case 0:
256       return @"";
257     case 2:
258       if (_cstring[0] == 'c' && _cstring[1] == 'c')
259         return HeaderNames->cc;
260       else if (_cstring[0] == 't' && _cstring[1] == 'o')
261         return HeaderNames->to;
262       break;
263     case 4:
264       if (_cstring[3] == 'e') {
265         if (strncmp(_cstring, "date", 4) == 0)
266           return HeaderNames->date;
267       }
268       else if (_cstring[3] == 'm') {
269         if (strncmp(_cstring, "from", 4) == 0)
270           return HeaderNames->from;
271       }
272       else if (_cstring[3] == 't') {
273         if (strncmp(_cstring, "host", 4) == 0)
274           return HeaderNames->host;
275       }
276       break;
277     case 6:
278       if (_cstring[5] == 't') {
279         if (strncmp(_cstring, "accept", 6) == 0)
280           return HeaderNames->accept;
281       }
282       if (_cstring[5] == 'e') {
283         if (strncmp(_cstring, "cookie", 6) == 0)
284           return HeaderNames->cookie;
285       }
286       break;
287     case 7:
288       if (_cstring[6] == 't') {
289         if (strncmp(_cstring, "subject", 7) == 0)
290           return HeaderNames->subject;
291       }
292       if (_cstring[6] == 'r') {
293         if (strncmp(_cstring, "referer", 7) == 0)
294           return HeaderNames->referer;
295       }
296       break;
297     case 8:
298       if (_cstring[5] == '-') {
299         if (strncmp(_cstring, "reply-to", 8) == 0)
300           return HeaderNames->replyTo;
301       }
302       if (_cstring[7] == 'd') {
303         if (strncmp(_cstring, "received", 8) == 0)
304           return HeaderNames->received;
305       }
306       if (_cstring[1] == '-') {
307         if (strncmp(_cstring, "x-mailer", 8) == 0)
308           return HeaderNames->xMailer;
309       }
310       break;
311     case 10:
312       if (_cstring[4] == '-') {
313         if (_cstring[6] == 'g') {
314           if (strncmp(_cstring, "user-agent", 10) == 0)
315             return HeaderNames->userAgent;
316         }
317         if (_cstring[6] == 'l') {
318           if (strncmp(_cstring, "keep-alive", 10) == 0)
319             return HeaderNames->keepAlive;
320         }
321       }
322       else if (_cstring[7] == '-') {
323         if (strncmp(_cstring, "message-id", 10) == 0)
324           return HeaderNames->messageID;
325       }
326       else if (_cstring[9] == 'n') {
327         if (strncmp(_cstring, "connection", 10) == 0)
328           return HeaderNames->connection;
329       }
330       break;
331     case 11:
332       if (_cstring[6] == '-') {
333         if (strncmp(_cstring, "return-path", 11) == 0)
334           return HeaderNames->returnPath;
335       }
336       break;
337     case 12:
338       if (_cstring[4] == '-') {
339         if (strncmp(_cstring, "mime-version", 12) == 0)
340           return HeaderNames->mimeVersion;
341       }
342       else if (_cstring[11] == 'n') {
343         if (strncmp(_cstring, "organization", 12) == 0)
344           return HeaderNames->organization;
345       }
346       else if (_cstring[7] == '-') {
347         if (strncmp(_cstring, "content-type", 12) == 0)
348           return HeaderNames->contentType;
349       }
350       break;
351     case 13:
352       if (_cstring[5] == '-') {
353         if (strncmp(_cstring, "cache-control", 13) == 0)
354           return HeaderNames->cacheControl;
355       }
356       break;
357     case 14:
358       if (_cstring[7] == '-') {
359         if (strncmp(_cstring, "content-length", 14) == 0) {
360           return HeaderNames->contentLength;
361         }
362       }
363       else if (_cstring[6] == '-') {
364         if (strncmp(_cstring, "accept-charset", 14) == 0)
365           return HeaderNames->acceptCharset;
366       }
367       break;
368     case 15:
369       if (_cstring[6] == '-') {
370         if (_cstring[7] == 'l') {
371           if (strncmp(_cstring, "accept-language", 15) == 0)
372             return HeaderNames->acceptLanguage;
373         }
374         else if (_cstring[7] == 'e') {
375           if (strncmp(_cstring, "accept-encoding", 15) == 0)
376             return HeaderNames->acceptEncoding;
377         }
378       }
379       break;
380     case 19:
381       if (_cstring[7] == '-') {
382         if (strncmp(_cstring, "content-disposition", 19) == 0)
383           return HeaderNames->contentDisposition;
384       }
385       break;
386     case 25:
387       if (_cstring[7] == '-') {
388         if (strncmp(_cstring, "content-transfer-encoding", 25) == 0)
389           return HeaderNames->contentTransferEncoding;
390       }
391       break;
392   }
393   {
394     NSString *result;
395
396     result = [NSString stringWithCString:_cstring length:_len];
397 #if DEBUG & 0    
398     if (MimeLogEnabled)
399       [self logWithFormat:@"%s: found no headerfield constant for <%@>, "
400             @"generate new string", __PRETTY_FUNCTION__, result];
401 #endif
402     return result;
403   }
404 }
405
406
407 - (NSString *)fieldNameForCString:(char *)_cstring length:(int)_len {
408   return fieldNameForCString(self, _cstring, _len);
409 }
410
411 - (NGHashMap *)parseHeader {
412   // TODO: split up this huge method!
413   /* parse headers until an empty line is seen */
414   NGMutableHashMap *header           = nil;
415   NSMutableData    *fieldValue       = nil;
416   NSMutableString  *fieldName        = nil;
417   NSString         *realFieldName    = nil;
418   BOOL             foundEndOfHeaders = NO;
419   int              bufCnt            = 0;
420   char             *buf              = NULL;
421   NSAutoreleasePool *pool;
422   
423   ASSIGN(self->contentTransferEncoding, (id)nil);
424   
425   if (self->delegateRespondsTo.parserWillParseHeader) {
426     if (![self->delegate parserWillParseHeader:self])
427       return nil;
428   }
429   
430   pool       = [[NSAutoreleasePool alloc] init];
431   fieldValue = [NSMutableData dataWithCapacity:512];
432   header     = [NGMutableHashMap hashMapWithCapacity:128];
433   buf        = calloc(self->bufLen, 1);
434   bufCnt     = *&bufCnt;
435   
436   while (!foundEndOfHeaders) {
437     int  c              = 0;
438     BOOL endOfFieldBody = NO;
439     
440     /* reset mutable vars */
441     
442     if (fieldName) {
443       [fieldName release];
444       fieldName = nil;
445     }
446     
447     [fieldValue setLength:0];
448     
449     /* parse fieldName */
450     {
451       unsigned fnlen;
452       BOOL lastWasCR;
453       
454       bufCnt    = 0;
455       fnlen     = 0;
456       lastWasCR = NO;
457       
458       while ((c = _la(self, 0)) != ':') {
459         if (c == -1)
460           /* EOF */
461           break;
462
463         /* check for leading '\r\n' or '\n' */
464         if (fnlen == 0) {
465           if (c == '\r') {
466             lastWasCR = YES;
467           }
468           else if (c == '\n') {
469             /* finish, found header starting with newline */
470             foundEndOfHeaders = YES;
471             endOfFieldBody    = YES;
472             self->useContentLength = NO;
473             _consume(self, 1); // consume newline
474             break; /* leave local loop */
475           }
476         }
477         else if ((fnlen == 1) && lastWasCR) {
478           if (c == '\n') {
479             /* finish, found \r\n */
480             foundEndOfHeaders = YES;
481             endOfFieldBody    = YES;
482             self->useContentLength = NO;
483             bufCnt = 0;
484             _consume(self, 1); // consume newline
485             break; /* leave local loop */
486           }
487         }
488         /* add to buffer */
489         buf[bufCnt] = c;
490         bufCnt++;
491         fnlen++;
492         
493         _consume(self, 1);
494       
495         if (bufCnt >= self->bufLen) {
496           register int i;
497
498           for (i = 0; i < bufCnt; i++)
499             buf[i] = tolower((int)buf[i]);
500
501           
502           if (fieldName == nil) {
503             fieldName = [[MStringClass alloc] initWithCString:buf length:bufCnt];
504           }
505           else {
506             NSString *s;
507
508             s = [[StringClass alloc] initWithCString:buf length:bufCnt];
509             [fieldName appendString:s];
510             [s release]; s = nil;
511           }
512           bufCnt = 0;
513         }
514       }
515       if (foundEndOfHeaders)
516         /* leave main loop */
517         break;
518       
519       if (bufCnt > 0) {
520         register int i;
521
522         for (i = 0; i < bufCnt; i++) 
523           buf[i] = tolower((int)buf[i]);
524         
525         if ([fieldName length] == 0) { 
526           /* const headernames are always smaller than bufLen */
527           realFieldName = fieldNameForCString(self, buf, bufCnt);
528         }
529         else {
530           NSString *s;
531
532           s = [[StringClass alloc] initWithCString:buf length:bufCnt];
533           [fieldName appendString:s];
534           [s release]; s = nil;
535           realFieldName = fieldName;
536           
537           if (c == -1) {
538             NSLog(@"WARNING(%s:%i): 1 an error occured during header-field "
539                   @" parsing (maybe end of stream) fieldName: %@",
540                   __PRETTY_FUNCTION__, __LINE__, fieldName);
541             foundEndOfHeaders = YES;
542             endOfFieldBody    = YES;
543           }
544         }
545       }
546       else {
547         realFieldName = fieldName;
548       }
549       _consume(self, 1);    // consume ':'
550     }
551     /* parse fieldBody */
552
553     bufCnt = 0;
554     while (!endOfFieldBody) {
555       int laC0 = _la(self, 0);
556       
557       if (laC0 == -1)
558         break;
559       
560       if (laC0 == '\r') {                        // CR
561         int laC1 = _la(self, 1);
562
563         if (isRfc822_LWSP(laC1)) {               // CR LSWSP
564           _consume(self, 2);  // folding
565         }
566         else if (laC1 == '\n') {                 // CR LF
567           int laC2 = _la(self, 2);
568
569           if (isRfc822_LWSP(laC2)) {             // CR LF LWSP
570             _consume(self, 3); // folding
571           }
572           else if (laC2 == '\r') {               // CR LF CR
573             int laC3 = _la(self, 3);
574
575             if (laC3 == '\n') {                  // CR LF CR LF
576               _consume(self, 4);
577               foundEndOfHeaders = YES;  // end of headers
578               endOfFieldBody    = YES;             
579             }
580             else {                               // CR LF CR *
581               _consume(self, 3); // ignored ??
582             }
583           }
584           else if (laC2 == '\n') {               // CR LF LF
585             _consume(self, 3);
586             foundEndOfHeaders = YES;  // end of headers
587             endOfFieldBody    = YES;            
588           }
589           else {                                 // CR LF *
590             _consume(self, 2);
591             endOfFieldBody = YES; //  next header field
592           }
593         }
594         else {                                   // CR *
595           _consume(self, 1);
596           endOfFieldBody = YES; // next header field
597         }
598       }
599       else  if (laC0 == '\n') {                  // LF
600         int laC1 = _la(self, 1);
601
602         if (isRfc822_LWSP(laC1)) {               // LF LWSP
603           _consume(self, 2); // folding
604         }
605         else if (laC1 == '\n') {                 // LF LF
606           _consume(self, 2);
607           foundEndOfHeaders = YES; // end of headers
608           endOfFieldBody    = YES; 
609         }
610         else if (laC1 == '\r') {                 // LF CR
611           int laC2 = _la(self, 2);
612           
613           if (isRfc822_LWSP(laC2)) {             // LF CR LWSP
614             _consume(self, 3); // folding
615           }
616           else if (laC2 == '\n') {               // LF CR LF
617             _consume(self, 3); //
618             foundEndOfHeaders = YES; // end of headers
619             endOfFieldBody    = YES; 
620           }
621           else {                                 // LF CR *
622             _consume(self, 2);
623             endOfFieldBody = YES; // next header field
624           }
625         }
626         else {                                   // LF *
627           _consume(self, 1);
628           endOfFieldBody = YES; // next header field
629         }
630       }
631       else {                                     // *
632         if ((bufCnt != 0) || (!isRfc822_LWSP(laC0))) {
633           /* ignore leading white spaces */
634           buf[bufCnt++] = laC0;
635         }
636         _consume(self, 1);
637         if (bufCnt >= self->bufLen) {
638           [fieldValue appendBytes:buf length:bufCnt];
639           bufCnt = 0;
640         }
641       }
642     }
643     if (bufCnt > 0) {
644       [fieldValue appendBytes:buf length:bufCnt];
645       bufCnt = 0;
646     }
647     if (!endOfFieldBody) {
648       [self logWithFormat:
649               @"WARNING(%s:%i): 2 an error occured during body parsing "
650               @"(maybe end of stream)", __PRETTY_FUNCTION__, __LINE__];
651       foundEndOfHeaders = YES;
652     }
653     if (realFieldName != nil) {
654       BOOL keepHeader = YES;
655
656       if (HeaderNames == NULL)
657         [NGMimePartParser headerFieldNames];
658
659       if (realFieldName == HeaderNames->contentTransferEncoding) {
660         int                 len;
661         const unsigned char *cstr;
662
663         len  = [fieldValue length];
664         cstr = [fieldValue bytes];
665           
666         keepHeader = NO; // don't keep content-tranfer-encodings
667           
668         while (isRfc822_LWSP(*cstr) && (len > 0)) { // strip leading spaces
669           cstr++;
670           len--;
671         }
672         if (len > 0) { // len==0 means the value was a string of LWSP
673           [self->contentTransferEncoding release];
674           self->contentTransferEncoding =
675             [[StringClass alloc] initWithCString:cstr length:len];
676         }
677         else
678           ASSIGN(self->contentTransferEncoding, (id)nil);
679       }
680       /*
681         take a look on content-length headers, since the parser
682         needs to know this for reading in the body ..
683       */
684       if (keepHeader && self->useContentLength) {
685         if (realFieldName == HeaderNames->contentLength) {
686           int                 len;
687           const unsigned char *cstr;
688           
689           len  = [fieldValue length];
690           cstr = [fieldValue bytes];
691
692           while (isRfc822_LWSP(*cstr) && (len > 0)) { // strip leading spaces
693             cstr++;
694             len--;
695           }
696           if (len > 0) { // len==0 means the value was a string of LWSP
697             unsigned char buf[len + 1];
698             int i = 0;
699
700             while (isdigit(*cstr) && (i < len)) { // extract following digits
701               buf[i++] = *cstr;
702               cstr++;
703             }
704             buf[i] = '\0'; // stop string after last digit (ignore the rest)
705             self->contentLength = atoi(buf);
706           }
707           else {
708             /* header value are only spaces */
709             self->contentLength = -1;
710           }
711         }
712       }
713       /* ask delegate if the header is to be kept */
714       if (keepHeader) {
715         if (self->delegateRespondsTo.parserKeepHeaderFieldData)
716           keepHeader = [self->delegate parser:self
717                                        keepHeaderField:realFieldName
718                                        data:fieldValue];
719       }
720       if (keepHeader) {
721         id value = nil;
722
723         value = [self valueOfHeaderField:realFieldName
724                       data:fieldValue];
725
726         if (value) {
727           value = [value retain];
728           /* ask delegate if the header is to be kept */
729           if (self->delegateRespondsTo.parserKeepHeaderFieldValue) {
730             keepHeader = [self->delegate parser:self
731                                          keepHeaderField:realFieldName
732                                          value:value];
733           }
734           if (keepHeader) {
735             NSAssert(realFieldName, @"missing field name ..");
736             NSAssert(value,     @"missing field value ..");
737
738             /*
739               check whether content-length, content-type,
740               subject already in hashmap
741             */
742             if (_checkKey(self, header, realFieldName))
743               [header addObject:value forKey:realFieldName];
744           }
745           [value release];
746         }
747       }
748     }
749   }
750   if (buf) {
751     free(buf);
752     buf = NULL;
753   }
754   
755   if (self->delegateRespondsTo.parserDidParseHeader)
756     [self->delegate parser:self didParseHeader:header];
757   
758   header = [header retain];
759   [pool release];
760   
761   return [header autorelease];
762 }
763
764 - (NSData *)readBodyUnknownLengthStream {
765   static NSMutableData *dataObject = nil;
766   NGIOReadMethodType readBytes = NULL;
767   NSData             *rbody;
768   NSMutableData      *body;
769   int  bufCnt;
770   char buf[self->bufLen];
771   void (*appendBytes)(id,SEL,const void *,unsigned);
772   BOOL decodeBase64;
773   
774   *(&readBytes) = NULL;
775   
776   if ([self->source respondsToSelector:@selector(methodForSelector:)]) {
777     readBytes = (NGIOReadMethodType)
778                 [self->source methodForSelector:@selector(readBytes:count:)];
779   }
780   
781   *(&appendBytes) = NULL;
782   *(&bufCnt)      = 0;
783   
784   // THREAD
785   /* check whether we can reuse the dataObj ... */
786   if (dataObject) {
787     *(&body) = [dataObject autorelease];
788     dataObject = nil; /* mark as used ... */
789   }
790   else {
791     *(&body) = [[[NSMutableData alloc] initWithCapacity:100010] autorelease];
792   }
793
794   decodeBase64 = NO;
795   appendBytes  = (void(*)(id,SEL,const void *, unsigned))
796     [body methodForSelector:@selector(appendBytes:length:)];
797   
798   NS_DURING {
799     while (YES) {
800       NSException *e;
801
802       NS_DURING {
803         _la(self, self->bufLen - 1);
804       }
805       NS_HANDLER {
806         if (![localException isKindOfClass:[NGEndOfStreamException class]])
807           [localException raise];
808       }
809       NS_ENDHANDLER;
810       
811       e = nil;
812       bufCnt = (readBytes != NULL)
813         ? readBytes(self->source, @selector(readBytes:count:),
814                     buf, self->bufLen)
815         : [self->source readBytes:buf count:self->bufLen];
816
817       if (bufCnt == NGStreamError) {
818         e = [self->source lastException];
819           
820         if ([e isKindOfClass:[NGEndOfStreamException class]])
821           /* leave loop */
822           break;
823         else
824           [e raise];
825       }
826       
827       /* perform any on-the-fly encodings */
828       
829       /* add to body data */
830       appendBytes(body, @selector(appendBytes:length:), buf, bufCnt);
831       bufCnt = 0;
832     }
833   }
834   NS_HANDLER {
835     if (![localException isKindOfClass:[NGEndOfStreamException class]])
836       [localException raise];
837   }
838   NS_ENDHANDLER;
839   if (bufCnt > 0 && bufCnt != NGStreamError) {
840     appendBytes(body, @selector(appendBytes:length:), buf, bufCnt);
841     bufCnt = 0;
842   }
843   
844   if (decodeBase64) {
845     ASSIGN(self->contentTransferEncoding, (id)nil);
846   }
847   rbody = [body copy];
848   // THREAD
849   /* remember that object for reuse ... */
850   if (dataObject == nil && [body length] < MAX_DATA_OBJECT_SIZE_CACHE) {
851     dataObject = [body retain];
852     [dataObject setLength:0];
853   }
854   
855   return [rbody autorelease];
856 }
857
858 - (NSData *)readBodyUnknownLengthData {
859   return [self->sourceData subdataWithRange:
860               NSMakeRange(self->dataIdx, self->byteLen - self->dataIdx)];
861 }
862
863 - (NSData *)readBodyUnknownLength {
864   return (self->source)
865     ? [self readBodyUnknownLengthStream]
866     : [self readBodyUnknownLengthData];
867 }
868
869 - (NSData *)readBodyWithKnownLengthFromStream:(unsigned)_len {
870   NGIOReadMethodType readBytes = NULL;
871   NSData             *rbody = nil;
872   unsigned char *buf = NULL;
873   int  readB     = 0;
874   
875   *(&readBytes) = NULL;
876   
877   if ([self->source respondsToSelector:@selector(methodForSelector:)]) {
878     readBytes = (NGIOReadMethodType)
879                 [self->source methodForSelector:@selector(readBytes:count:)];
880   }
881   
882
883   *(&buf) = NULL;
884   readB   = 0;
885     
886   buf = calloc(_len, sizeof(char));
887     
888   NS_DURING {
889
890     NS_DURING {
891     if (self->contentLength > self->bufLen)
892       _la(self, self->bufLen - 1);
893     else
894       _la(self, self->contentLength - 1);
895     }
896     NS_HANDLER {
897       if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
898         fprintf(stderr,
899                 "WARNING(%s): EOF occurred before whole content was read "
900                 "(content-length=%i, read=%i)\n", __PRETTY_FUNCTION__,
901                 self->contentLength, readB);
902       }
903       else {
904         if (buf) free(buf);
905         [localException raise];
906       }
907     }
908     NS_ENDHANDLER;
909       
910       
911     while (self->contentLength != readB) {
912       int tmp = self->contentLength - readB;
913         
914       readB += (readBytes != NULL)
915         ? readBytes(self->source, @selector(readBytes:count:),
916                     (buf + readB), tmp)
917         : [self->source readBytes:(buf + readB) count:tmp];
918
919       if (readB == NGStreamError) {
920         [[self->source lastException] raise];
921       }
922         
923       tmp = self->contentLength - readB;
924       if (tmp > 0) {
925         if (tmp > self->bufLen)
926           _la(self, self->bufLen - 1);
927         else
928           _la(self, tmp - 1);
929       }
930     }
931   }
932   NS_HANDLER {
933     if ([localException isKindOfClass:[NGEndOfStreamException class]]) {
934       fprintf(stderr,
935               "WARNING(%s): EOF occurred before whole content was read "
936               "(content-length=%i, read=%i)\n", __PRETTY_FUNCTION__,
937               self->contentLength, readB);
938     }
939     else {
940       if (buf) free(buf);
941       [localException raise];
942     }
943   }
944   NS_ENDHANDLER;
945   
946   rbody = buf ? [NSData dataWithBytes:buf length:readB] : nil;
947   if (buf) free(buf);
948   return rbody;
949 }
950
951 - (NSData *)readBodyWithKnownLengthFromData:(unsigned)_len {
952   NSData *data;
953
954   data = [self->sourceData subdataWithRange:
955               NSMakeRange(self->dataIdx, self->byteLen - self->dataIdx)];
956   if ([data length] != _len) {
957     NSLog(@"%s[%i]: got wrong data %d _len %d", __PRETTY_FUNCTION__, __LINE__,
958           [data length], _len);
959     return nil;
960   }
961   return data;
962 }
963
964 - (NSData *)readBodyWithKnownLength:(unsigned)_len {
965   return (self->source)
966     ? [self readBodyWithKnownLengthFromStream:_len]
967     : [self readBodyWithKnownLengthFromData:_len];
968 }
969
970 - (NSData *)applyTransferEncoding:(NSString *)_encoding onData:(NSData *)_data{
971   // TODO: make this an NSData category
972   unsigned len;
973   unichar  c;
974   
975   if ((len = [_encoding length]) == 0)
976     return nil;
977   
978   _encoding = [_encoding lowercaseString];
979   
980   c = [_encoding characterAtIndex:0];
981   switch (c) {
982   case 'q':
983     if ([_encoding hasPrefix:@"quoted"])
984       return [_data dataByDecodingQuotedPrintable];
985     break;
986   case 'b':
987     if ([_encoding hasPrefix:@"base64"])
988       return [_data dataByDecodingBase64];
989     else if ([@"binary" isEqualToString:_encoding])
990       return _data;
991     break;
992   case '7':
993   case '8':
994   case 'i':
995     if (len == 4) {
996       if ([@"7bit" isEqualToString:_encoding])
997         return _data;
998       if ([@"8bit" isEqualToString:_encoding])
999         return _data;
1000       break;
1001     }
1002     else if (len == 8) {
1003       if ([@"identity" isEqualToString:_encoding])
1004         return _data;
1005     }
1006
1007   case 'u':
1008     if (len == 12) {
1009       if ([@"unknown-8bit" isEqualToString:_encoding])
1010         return _data;
1011     }
1012   default:
1013     break;
1014   }
1015   
1016   return nil;
1017 }
1018
1019 - (NSData *)readBody {
1020   /* Read data of body and apply content-transfer-encoding if required. */
1021   NSAutoreleasePool *pool;
1022   NSData            *rbody = nil;
1023   
1024   pool = [[NSAutoreleasePool alloc] init];
1025
1026   if ((self->contentLength == -1) || (self->contentLength == 0)) {
1027     rbody = [self readBodyUnknownLength];
1028   }
1029   else {
1030     /* note: this is called only, if self->useContentLength is set ! */
1031     rbody = [self readBodyWithKnownLength:self->contentLength];
1032   }
1033
1034   if ([self->contentTransferEncoding length] > 0) {
1035     NSData *new;
1036     
1037     new = [self applyTransferEncoding:self->contentTransferEncoding
1038                 onData:rbody];
1039     if (new) {
1040       ASSIGN(self->contentTransferEncoding, (id)nil);
1041       rbody = new;
1042     }
1043     else {
1044       [self logWithFormat:@"WARNING(%s): "
1045               @"encountered unknown content-transfer-encoding: '%@'",
1046               __PRETTY_FUNCTION__,
1047               self->contentTransferEncoding];
1048     }
1049   }
1050   
1051   rbody = [rbody retain];
1052   [pool release];
1053   return [rbody autorelease];
1054 }
1055
1056 - (NSData *)decodeBody:(NSData *)_data ofPart:(id<NGMimePart>)_part {
1057   return (self->delegateRespondsTo.parserDecodeBodyOfPart)
1058     ? [self->delegate parser:self decodeBody:_data ofPart:_part]
1059     : _data;
1060 }
1061
1062 - (NGMimeType *)defaultContentTypeForPart:(id<NGMimePart>)_part {
1063   static NGMimeType *octetType = nil;
1064   
1065   if (octetType == nil)
1066     octetType = [[NGMimeType mimeType:@"application/octet-stream"] retain];
1067   return octetType;
1068 }
1069
1070 - (id<NGMimeBodyParser>)parserForBodyOfPart:(id<NGMimePart>)_p
1071   data:(NSData *)_dt
1072 {
1073   id                   ctype;
1074   NGMimeType           *contentType;
1075   id<NGMimeBodyParser> bodyParser   = nil;
1076   
1077   ctype = [_p contentType];
1078   
1079   contentType = ([ctype isKindOfClass:[NGMimeType class]])
1080     ? ctype
1081     : [NGMimeType mimeType:[ctype stringValue]];
1082   
1083   if (self->delegateRespondsTo.parserBodyParserForPart) {
1084     if ((bodyParser = [self->delegate parser:self bodyParserForPart:_p]))
1085       return bodyParser;
1086   }
1087   
1088   if (contentType == nil) {
1089     contentType = [self defaultContentTypeForPart:_p];
1090   }
1091   
1092   if (contentType) {
1093     if ([[contentType type] isEqualToString:@"multipart"]) {
1094       bodyParser = [[[NGMimeMultipartBodyParser alloc] init] autorelease];
1095     }
1096     else if ([[contentType type] isEqualToString:@"text"] &&
1097              [[contentType subType] isEqualToString:@"plain"]) {
1098       bodyParser = [[[NGMimeTextBodyParser alloc] init] autorelease];
1099     }
1100   }
1101   return bodyParser;
1102 }
1103
1104 - (void)parseBodyOfPart:(id<NGMimePart>)_part {
1105   NGMimeBodyParser *parser  = nil;
1106   NSData           *rawBody = nil;
1107   id               body     = nil;
1108
1109   rawBody = [self readBody];
1110
1111   /* apply content-encoding, transfer-encoding and similiar */
1112   rawBody = [self decodeBody:rawBody ofPart:_part];
1113
1114   if (self->delegateRespondsTo.parserParseRawBodyDataOfPart) {
1115     BOOL didParse;
1116
1117     didParse =
1118       [self->delegate parser:self parseRawBodyData:rawBody ofPart:_part];
1119
1120     if (didParse) return;
1121   }
1122   
1123   parser = (NGMimeBodyParser *)[self parserForBodyOfPart:_part data:rawBody];
1124   if (parser) {
1125     /* make sure delegate keeps being around .. */
1126     self->delegate = [[self->delegate retain] autorelease];
1127
1128     body = [parser parseBodyOfPart:_part
1129                    data:rawBody
1130                    delegate:self->delegate];
1131   }
1132   else if (rawBody) { /* no parser found for body */
1133     if (body == nil) body = rawBody;
1134   }
1135   [_part setBody:body];
1136 }
1137
1138 /* part */
1139
1140 - (id<NGMimePart>)producePartWithHeader:(NGHashMap *)_header {
1141   [self subclassResponsibility:_cmd];
1142   return nil;
1143 }
1144
1145 - (BOOL)prepareForParsingFromData:(NSData *)_data {
1146   if (_data == nil)
1147     return NO;
1148
1149   ASSIGN(self->sourceData, _data);
1150   self->sourceBytes   = [self->sourceData bytes];
1151   self->byteLen       = [self->sourceData length];
1152   self->dataIdx       = 0;
1153   self->contentLength = -1;
1154
1155   return YES;
1156 }
1157
1158 - (BOOL)prepareForParsingFromStream:(id<NGStream>)_stream {
1159   if (_stream == nil)
1160     return NO;
1161   
1162   if (self->source != _stream) {
1163     NGByteBuffer *bb;
1164
1165     bb = [NGByteBuffer alloc];
1166     bb = [bb initWithSource:_stream la:self->bufLen];
1167     [self->source release];
1168     self->source = bb;
1169   }
1170   if ([self->source respondsToSelector:@selector(methodForSelector:)]) {
1171     self->la         = (int (*)(id, SEL, unsigned))
1172                        [self->source methodForSelector:@selector(la:)];
1173     self->consume    = (void (*)(id, SEL))
1174                        [self->source methodForSelector:@selector(consume)];
1175     self->consumeCnt = (void (*)(id, SEL, unsigned))
1176                        [self->source methodForSelector:@selector(consume:)];
1177   }
1178   else {
1179     self->la         = NULL;
1180     self->consume    = NULL;
1181     self->consumeCnt = NULL;
1182   }
1183   self->contentLength = -1;
1184
1185   return YES;
1186 }
1187
1188 - (void)finishParsingOfPart:(id<NGMimePart>)_part {
1189   [self->source release]; self->source = nil;
1190   self->contentLength = -1;
1191   
1192   self->la         = NULL;
1193   self->consume    = NULL;
1194   self->consumeCnt = NULL;
1195 }
1196
1197 - (void)finishParsingOfPartFromData:(id<NGMimePart>)_part {
1198   [self->sourceData release]; self->sourceData = nil;
1199   self->sourceBytes   = NULL;
1200   self->byteLen       = 0;
1201   self->dataIdx       = 0;
1202   self->contentLength = -1;
1203 }
1204
1205 - (BOOL)parsePrefix {
1206   return YES;
1207 }
1208
1209 - (void)parseSuffix {
1210 }
1211
1212 - (id<NGMimePart>)parsePart {
1213   id<NGMimePart> part = nil;
1214   NGHashMap *header;
1215   BOOL      doParse = YES;
1216   
1217   if (![self parsePrefix])
1218     return nil;
1219   
1220   if ((header = [self parseHeader]) == nil)
1221     return nil;
1222   
1223   part = [self producePartWithHeader:header];
1224   
1225   doParse = (delegateRespondsTo.parserWillParseBodyOfPart)
1226     ? [delegate parser:self willParseBodyOfPart:part]
1227     : YES;
1228   
1229   if (doParse) {
1230     NSAutoreleasePool *pool;
1231
1232     pool = [[NSAutoreleasePool alloc] init];
1233     [self parseBodyOfPart:part];
1234     [pool release];
1235     
1236     if (delegateRespondsTo.parserDidParseBodyOfPart)
1237       [delegate parser:self didParseBodyOfPart:part];
1238     
1239     [self parseSuffix];
1240   }
1241   return part;
1242 }
1243
1244 - (id<NGMimePart>)parsePartFromStream:(id<NGStream>)_stream {
1245   id<NGMimePart> p;
1246   
1247   if (![self prepareForParsingFromStream:_stream])
1248     return nil;
1249   
1250   p = [self parsePart];
1251   [self finishParsingOfPart:p];
1252   return p;
1253 }
1254
1255 - (id<NGMimePart>)parsePartFromData:(NSData *)_data {
1256   id<NGMimePart> part;
1257   
1258   if ([_data isKindOfClass:NSMutableDataClass]) {
1259     NGDataStream *dataStream;
1260   
1261     dataStream = [NGDataStream streamWithData:_data];
1262     part = [self parsePartFromStream:dataStream];
1263     [dataStream close];
1264     return part;
1265   }
1266
1267   if ([self prepareForParsingFromData:_data]) {
1268     part = [self parsePart];
1269     [self finishParsingOfPartFromData:part];
1270     return part;
1271   }
1272   
1273   return nil;
1274 }
1275
1276 /* accessors */
1277
1278 - (BOOL)doesUseContentLength {
1279   return self->useContentLength;
1280 }
1281 - (void)setUseContentLength:(BOOL)_use {
1282   self->useContentLength = _use;
1283 }
1284
1285 /* functions */
1286
1287 static inline int _la(NGMimePartParser *self, int _la) {
1288   if (self->source) {
1289     return (self->la != NULL) ? self->la(self->source, @selector(la:), _la)
1290       : [self->source la:_la];
1291   }
1292   else {
1293     if ((self->dataIdx+_la) < self->byteLen)
1294       return self->sourceBytes[self->dataIdx+_la];
1295     else
1296       return -1;
1297   }
1298 }
1299
1300 static inline void _consume(NGMimePartParser *self, int _cnt) {
1301   if (self->source) {
1302     if (_cnt == 1) {
1303       if (self->consume != NULL)
1304         self->consume(self->source, @selector(consume));
1305       else
1306         [self->source consume];
1307     }
1308     else {
1309       if (self->consumeCnt != NULL)
1310         self->consumeCnt(self->source, @selector(consume:), _cnt);
1311       else
1312         [self->source consume:_cnt];
1313     }
1314   }
1315   else {
1316     if ((self->dataIdx+_cnt) <= self->byteLen) {
1317       self->dataIdx += _cnt;
1318     }
1319     else {
1320       NSLog(@"%s[%i]: error try to read over buffer len self->dataIdx %d "
1321             @"_cnt %d byteLen %d", __PRETTY_FUNCTION__, __LINE__,
1322             self->dataIdx, _cnt, self->byteLen);
1323     }
1324   }
1325 }
1326
1327 static inline BOOL _checkKey(NGMimePartParser *self, NGHashMap *_map,
1328                              NSString *_key)
1329 {
1330   if (HeaderNames == NULL)
1331     [NGMimePartParser headerFieldNames];
1332   
1333   if  ((_key == HeaderNames->contentLength) ||
1334        _key == HeaderNames->contentType) {
1335     if ([_map countObjectsForKey:_key] > 0)
1336       return NO;
1337   }
1338   return YES;
1339 }
1340  
1341 @end /* NGMimePartParser */