]> err.no Git - sope/blob - sope-mime/NGMail/NGMBoxReader.m
use %p for pointer formats
[sope] / sope-mime / NGMail / NGMBoxReader.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
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
9   later version.
10
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.
15
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
19   02111-1307, USA.
20 */
21
22 #include "NGMBoxReader.h"
23 #include "NGMimeMessageParser.h"
24 #include "common.h"
25 #include <string.h>
26
27 @implementation NGMBoxReader
28
29 + (int)version {
30   return 2;
31 }
32
33 static inline int __readByte(NGMBoxReader *self);
34 static inline void
35 __appendByte(NGMBoxReader *self, NSMutableData *_data, IMP _readBytes, int _c);
36
37 + (id)readerForMBox:(NSString *)_path {
38   NGFileStream     *fs;
39   NGBufferedStream *bs;
40
41   fs = [NGFileStream alloc]; /* to keep gcc 3.4 happy */
42   fs = [[fs initWithPath:_path] autorelease];
43   bs = [NGBufferedStream filterWithSource:fs];
44
45   [fs openInMode:NGFileReadOnly];
46
47   return [[(NGMBoxReader *)[self alloc] initWithSource:bs] autorelease];
48 }
49
50 + (id)mboxWithSource:(id<NGByteSequenceStream>)_in {
51   return [[(NGMBoxReader *)[self alloc] initWithSource:_in] autorelease];
52 }
53
54 - (id)init {
55   return [self initWithSource:nil];
56 }
57
58 - (id)initWithSource:(id<NGByteSequenceStream>)_in {
59   if ((self = [super init])) {
60     self->source        = [_in retain];
61     self->isEndOfStream = NO;
62     self->lastDate      = nil;
63     self->separator     = @"From -";
64
65     self->readByte =
66       [self->source respondsToSelector:@selector(methodForSelector:)]
67       ? [(NSObject *)self->source methodForSelector:@selector(readByte)]
68       : NULL;
69   }
70   return self;
71 }
72
73 - (void)dealloc {
74   [self->source    release];
75   [self->lastDate  release];
76   [self->separator release];
77   self->readByte = NULL;
78   [super dealloc];
79 }
80
81 - (id<NGMimePart>)nextMessage {
82
83   // Macros
84 #define AppendBytes(_buf, _cnt) \
85   appendBytes(msgData, @selector(appendBytes:length:), _buf, _cnt)
86
87 #define AppendByte(_c) \
88   __appendByte(self, msgData, appendBytes, _c)
89
90   // Method  
91   NSMutableData       *msgData    = nil;
92   IMP                 appendBytes = NULL;
93
94   const int bufLen = 256;
95   int       bufCnt = 0;
96   int       c      = 0;
97   char      buf[bufLen];  
98   
99   int        sepLength  = [self->separator length];
100   const char *sepStrbuf = NULL;
101
102   sepStrbuf = [self->separator cString];
103   
104   msgData     = [[NSMutableData allocWithZone:[self zone]]
105                                 initWithCapacity:4096];
106   
107   appendBytes = [msgData methodForSelector:@selector(appendBytes:length:)];
108   //read from-line
109
110   if (self->isEndOfStream)
111     return nil;
112
113   if (self->lastDate == nil) {
114     // start of MBox from-line length < 255
115     bufCnt = 0;
116     while (bufCnt < sepLength) { // parse form
117       c = __readByte(self);
118       buf[bufCnt++] = c;
119     }
120     if (strncmp(buf, sepStrbuf, sepLength) != 0) {
121       NSLog(@"WARNING: no %@ at begin of MBox %s", self->separator, buf);
122     }
123     bufCnt = 0;
124     while ((c = __readByte(self)) != '\n') { // parse date < 255
125       buf[bufCnt++] = c;
126       if (bufCnt >= bufLen) {
127         NSLog(@"WARNING: too long from-line");
128         break;
129       }
130     }
131     if (buf[bufCnt - 1] == '\r')
132       self->lastDate = [[NSString allocWithZone:[self zone]] initWithCString:buf
133                                                              length:bufCnt-1];
134     else
135       self->lastDate = [[NSString allocWithZone:[self zone]] initWithCString:buf
136                                                              length:bufCnt];
137     bufCnt = 0;
138   }
139   c = -2;
140   do {
141     if (c != -2) {     
142       AppendBytes(buf, bufCnt); // write buffer to data
143       bufCnt = 0;
144       if (c != '\n') { // no end of line
145         AppendByte(c);
146         while ((c = __readByte(self)) != '\n') {
147           buf[bufCnt++] = c;
148           if (bufCnt >= bufLen) {
149             AppendBytes(buf, bufCnt);
150             bufCnt = 0;
151           }
152         }
153         if (bufCnt > 0) {
154           AppendBytes(buf, bufCnt);
155           bufCnt = 0;
156         }
157         AppendByte(c);
158       }
159       else
160         AppendByte(c);
161     }
162     
163     while ((c = __readByte(self)) != '\n' &&
164            bufCnt < sepLength) {        // read oly until seperator length    
165       if (c == -1)
166         break;
167       buf[bufCnt++] = c;
168     }
169     if (c == -1)
170       break;
171   } while (strncmp(buf, sepStrbuf, sepLength) != 0);
172     
173   if (c == -1) {
174     self->isEndOfStream = YES;
175   } 
176   else { // read from-line
177     bufCnt = 0;
178     while ((c = __readByte(self)) != '\n') { // from-line is not longer
179                                      // than 255 ( I hope it :))
180       buf[bufCnt++] = c;
181       if (bufCnt >= bufLen) {
182         NSLog(@"WARNING: too long from-line");
183         break;
184       }
185     }
186     [self->lastDate release];
187     self->lastDate = [[NSString alloc] initWithCString:buf length:bufCnt];
188     bufCnt = 0;
189   }
190
191   if ([msgData length] == 0) // end, no msg data
192     return nil;
193       
194   {  // build result
195     NGMimeMessageParser *parser = nil;
196     NGDataStream        *stream = nil;
197     id<NGMimePart> part;
198
199     *(&part) = nil;
200     
201     parser = [[NGMimeMessageParser alloc] init];
202     stream = [[NGDataStream alloc] initWithData:msgData];
203
204     NS_DURING
205       part = [parser parsePartFromStream:stream];
206     NS_HANDLER {}
207     NS_ENDHANDLER;
208
209     if (part == nil) {
210       fprintf(stderr, "mbox: failed to parse message:\n%s",
211               [[NSString stringWithCString:[msgData bytes]
212                          length:[msgData length]] cString]);
213     }
214
215     [parser  release]; parser  = nil;
216     [stream  release]; stream  = nil;    
217     [msgData release]; msgData = nil;
218     
219     return part;
220   }
221 }
222
223 /* description */
224
225 - (NSString *)description {
226   return [NSString stringWithFormat:@"<%@[0x%p] source=%@ endOfStream=%@",
227                      NSStringFromClass([self class]), (unsigned)self,
228                      self->source, self->isEndOfStream ? @"YES" : @"NO"];
229 }
230
231
232 /* functions */
233
234 static inline int __readByte(NGMBoxReader *self) {
235   return (self->readByte)
236     ? (int)self->readByte(self->source, @selector(readByte))
237     : [self->source readByte];
238 }
239
240 static inline void __appendByte(NGMBoxReader *self, NSMutableData *_data,
241                                 IMP _readBytes, int _c) {
242   unsigned char c = _c;
243   _readBytes(_data, @selector(appendBytes:length:), &c, 1);
244 }
245
246 @end