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