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